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.cab.document.service.impl;
017
018 import java.util.ArrayList;
019 import java.util.HashMap;
020 import java.util.Iterator;
021 import java.util.List;
022 import java.util.Map;
023
024 import org.apache.commons.lang.StringUtils;
025 import org.apache.log4j.Logger;
026 import org.kuali.kfs.integration.purap.CapitalAssetLocation;
027 import org.kuali.kfs.integration.purap.ItemCapitalAsset;
028 import org.kuali.kfs.module.cab.CabConstants;
029 import org.kuali.kfs.module.cab.CabPropertyConstants;
030 import org.kuali.kfs.module.cab.businessobject.GeneralLedgerEntry;
031 import org.kuali.kfs.module.cab.businessobject.Pretag;
032 import org.kuali.kfs.module.cab.businessobject.PretagDetail;
033 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableDocument;
034 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableItemAsset;
035 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableLineAssetAccount;
036 import org.kuali.kfs.module.cab.document.service.PurApInfoService;
037 import org.kuali.kfs.module.cab.document.service.PurApLineDocumentService;
038 import org.kuali.kfs.module.cab.document.service.PurApLineService;
039 import org.kuali.kfs.module.cab.document.web.PurApLineSession;
040 import org.kuali.kfs.module.cam.CamsConstants;
041 import org.kuali.kfs.module.cam.CamsPropertyConstants;
042 import org.kuali.kfs.module.cam.businessobject.Asset;
043 import org.kuali.kfs.module.cam.businessobject.AssetGlobal;
044 import org.kuali.kfs.module.cam.businessobject.AssetGlobalDetail;
045 import org.kuali.kfs.module.cam.businessobject.AssetPaymentAssetDetail;
046 import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail;
047 import org.kuali.kfs.module.cam.businessobject.AssetType;
048 import org.kuali.kfs.module.cam.businessobject.defaultvalue.NextAssetNumberFinder;
049 import org.kuali.kfs.module.cam.document.AssetPaymentDocument;
050 import org.kuali.kfs.module.cam.document.service.AssetGlobalService;
051 import org.kuali.kfs.module.cam.document.service.AssetService;
052 import org.kuali.kfs.module.purap.PurapPropertyConstants;
053 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderCapitalAssetSystem;
054 import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
055 import org.kuali.kfs.sys.businessobject.Building;
056 import org.kuali.kfs.sys.businessobject.Room;
057 import org.kuali.kfs.sys.context.SpringContext;
058 import org.kuali.rice.kew.exception.WorkflowException;
059 import org.kuali.rice.kns.bo.Campus;
060 import org.kuali.rice.kns.document.MaintenanceDocument;
061 import org.kuali.rice.kns.service.BusinessObjectService;
062 import org.kuali.rice.kns.service.DocumentService;
063 import org.kuali.rice.kns.service.KualiModuleService;
064 import org.kuali.rice.kns.util.KNSConstants;
065 import org.kuali.rice.kns.util.KualiDecimal;
066 import org.kuali.rice.kns.util.ObjectUtils;
067
068
069 /**
070 * This class provides default implementations of {@link PurApLineService}
071 */
072 public class PurApLineDocumentServiceImpl implements PurApLineDocumentService {
073 private static final Logger LOG = Logger.getLogger(PurApLineDocumentServiceImpl.class);
074 private BusinessObjectService businessObjectService;
075 private DocumentService documentService;
076 private PurApLineService purApLineService;
077 private PurApInfoService purApInfoService;
078 private AssetGlobalService assetGlobalService;
079
080 public static final String DOCUMENT_DESC_PREFIX = "CAB created for ";
081
082 /**
083 * @see org.kuali.kfs.module.cab.document.service.PurApLineDocumentService#processApplyPayment(PurchasingAccountsPayableItemAsset,
084 * List, PurApLineSession, Integer)
085 */
086 public String processApplyPayment(PurchasingAccountsPayableItemAsset selectedItem, List<PurchasingAccountsPayableDocument> purApDocs, PurApLineSession purApLineSession, Integer requisitionIdentifer) throws WorkflowException {
087 AssetPaymentDocument newDocument = (AssetPaymentDocument) documentService.getNewDocument(AssetPaymentDocument.class);
088 if (ObjectUtils.isNotNull(selectedItem) && ObjectUtils.isNotNull(selectedItem.getPurchasingAccountsPayableDocument())) {
089 newDocument.getDocumentHeader().setDocumentDescription(DOCUMENT_DESC_PREFIX + selectedItem.getPurchasingAccountsPayableDocument().getDocumentTypeCode() + " " + selectedItem.getDocumentNumber());
090 }
091 // set assetPaymentDetail list
092 createAssetPaymentDetails(newDocument.getSourceAccountingLines(), selectedItem, newDocument.getDocumentNumber(), requisitionIdentifer);
093
094 // If PurAp user entered capitalAssetNumbers, include them in the Asset Payment Document.
095 if (selectedItem.getPurApItemAssets() != null && !selectedItem.getPurApItemAssets().isEmpty()) {
096 createAssetPaymentAssetDetails(newDocument.getAssetPaymentAssetDetail(), selectedItem, newDocument.getDocumentNumber());
097
098 }
099 // set origin code in the Asset Payment Document
100 newDocument.setCapitalAssetBuilderOriginIndicator(true);
101 documentService.saveDocument(newDocument);
102
103 postProcessCreatingDocument(selectedItem, purApDocs, purApLineSession, newDocument.getDocumentNumber());
104 return newDocument.getDocumentNumber();
105 }
106
107
108 /**
109 * Create AssetPaymentAssetDetail List for assetPaymentDocument.
110 *
111 * @param assetPaymentAssetDetails
112 * @param selectedItem
113 * @param documentNumber
114 */
115 protected void createAssetPaymentAssetDetails(List assetPaymentAssetDetails, PurchasingAccountsPayableItemAsset selectedItem, String documentNumber) {
116 for (ItemCapitalAsset capitalAssetNumber : selectedItem.getPurApItemAssets()) {
117 // check if capitalAssetNumber is a valid value or not.
118 if (isAssetNumberValid(capitalAssetNumber.getCapitalAssetNumber())) {
119 AssetPaymentAssetDetail assetDetail = new AssetPaymentAssetDetail();
120 assetDetail.setDocumentNumber(documentNumber);
121
122 assetDetail.setCapitalAssetNumber(capitalAssetNumber.getCapitalAssetNumber());
123 assetDetail.refreshReferenceObject(CamsPropertyConstants.AssetPaymentAssetDetail.ASSET);
124
125 AssetService assetService = SpringContext.getBean(AssetService.class);
126 Asset candidateAsset = assetDetail.getAsset();
127 // asset must be an active & not retired. Duplication check is done during feeding asset numbers from PurAp.
128 if (ObjectUtils.isNotNull(candidateAsset) && assetService.isCapitalAsset(candidateAsset) && !assetService.isAssetRetired(candidateAsset)) {
129 assetDetail.setPreviousTotalCostAmount(assetDetail.getAsset().getTotalCostAmount());
130 assetPaymentAssetDetails.add(assetDetail);
131 }
132
133 }
134 }
135 }
136
137
138 /**
139 * Check the asset table if given capitalAssetNumber is valid or not.
140 *
141 * @param capitalAssetNumber
142 * @return
143 */
144 protected boolean isAssetNumberValid(Long capitalAssetNumber) {
145 Map pKeys = new HashMap<String, Object>();
146
147 pKeys.put(CamsPropertyConstants.Asset.CAPITAL_ASSET_NUMBER, capitalAssetNumber);
148
149 Asset asset = (Asset) businessObjectService.findByPrimaryKey(Asset.class, pKeys);
150
151 return ObjectUtils.isNotNull(asset);
152 }
153
154
155 /**
156 * @see org.kuali.kfs.module.cab.document.service.PurApLineService#processCreateAsset(org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableItemAsset,
157 * org.kuali.kfs.module.cab.document.web.struts.PurApLineForm)
158 */
159 public String processCreateAsset(PurchasingAccountsPayableItemAsset selectedItem, List<PurchasingAccountsPayableDocument> purApDocs, PurApLineSession purApLineSession, Integer requisitionIdentifier) throws WorkflowException {
160 // Create new CAMS asset global document
161 MaintenanceDocument newDocument = (MaintenanceDocument) documentService.getNewDocument(CamsConstants.DocumentTypeName.ASSET_ADD_GLOBAL);
162 newDocument.getNewMaintainableObject().setMaintenanceAction(KNSConstants.MAINTENANCE_NEW_ACTION);
163 if (ObjectUtils.isNotNull(selectedItem) && ObjectUtils.isNotNull(selectedItem.getPurchasingAccountsPayableDocument())) {
164 newDocument.getDocumentHeader().setDocumentDescription(DOCUMENT_DESC_PREFIX + selectedItem.getPurchasingAccountsPayableDocument().getDocumentTypeCode() + " " + selectedItem.getDocumentNumber());
165 }
166
167 // populate pre-tagging entry
168 Integer poId = selectedItem.getPurchasingAccountsPayableDocument().getPurchaseOrderIdentifier();
169 Pretag preTag = purApLineService.getPreTagLineItem(poId, selectedItem.getItemLineNumber());
170
171 // create asset global BO instance
172 AssetGlobal assetGlobal = createAssetGlobal(selectedItem, newDocument.getDocumentNumber(), preTag, requisitionIdentifier);
173
174 // save asset global BO to the document
175 newDocument.getNewMaintainableObject().setBusinessObject(assetGlobal);
176 documentService.saveDocument(newDocument);
177
178 postProcessCreatingDocument(selectedItem, purApDocs, purApLineSession, newDocument.getDocumentNumber());
179
180 // Save for in-active pre-tag detail if it got feed into CAMS
181 if (isItemPretagged(preTag)) {
182 businessObjectService.save(preTag);
183 }
184 return newDocument.getDocumentNumber();
185 }
186
187
188 /**
189 * Process item line, cab document after creating CAMs document.
190 *
191 * @param selectedItem
192 * @param purApForm
193 * @param purApLineSession
194 * @param documentNumber
195 */
196 protected void postProcessCreatingDocument(PurchasingAccountsPayableItemAsset selectedItem, List<PurchasingAccountsPayableDocument> purApDocs, PurApLineSession purApLineSession, String documentNumber) {
197 // save CAMS document number in CAB
198 selectedItem.setCapitalAssetManagementDocumentNumber(documentNumber);
199
200 // in-activate item, item account and glEntry(conditionally)
201 inActivateItem(selectedItem);
202
203 // update submit amount in the associated general ledger entries.
204 updateGlEntrySubmitAmount(selectedItem, purApLineSession.getGlEntryUpdateList());
205
206 // in-activate document if all the associated items are inactive.
207 if (ObjectUtils.isNotNull(selectedItem.getPurchasingAccountsPayableDocument())) {
208 // update document status code as 'Enroute' when all its items are in CAMs. Link reference from item to document should
209 // be set in PurApLineAction.getSelectedLineItem().
210 conditionalyUpdateDocumentStatusAsEnroute(selectedItem.getPurchasingAccountsPayableDocument());
211 }
212
213 // persistent to the table
214 purApLineService.processSaveBusinessObjects(purApDocs, purApLineSession);
215 // In-activate general ledger afterwards because we don't maintain the non-persistent relationship from GL to account, so
216 // account need to persistent changes first.
217 List<GeneralLedgerEntry> glEntryUpdatesList = getGlEntryInActivedList(selectedItem);
218 if (glEntryUpdatesList != null && !glEntryUpdatesList.isEmpty()) {
219 businessObjectService.save(glEntryUpdatesList);
220 }
221 }
222
223
224 /**
225 * set doc status as enroute when all its items are in CAMs
226 *
227 * @param selectedDoc
228 */
229 protected void conditionalyUpdateDocumentStatusAsEnroute(PurchasingAccountsPayableDocument selectedDoc) {
230 for (PurchasingAccountsPayableItemAsset item : selectedDoc.getPurchasingAccountsPayableItemAssets()) {
231 if (item.isActive()) {
232 return;
233 }
234 }
235
236 selectedDoc.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
237 }
238
239
240 /**
241 * Update transactionLedgerSubmitAmount in the associated generalLedgerEntry for each item account.
242 *
243 * @param selectedItem
244 */
245 protected void updateGlEntrySubmitAmount(PurchasingAccountsPayableItemAsset selectedItem, List glEntryList) {
246 GeneralLedgerEntry glEntry = null;
247 for (PurchasingAccountsPayableLineAssetAccount account : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
248 glEntry = account.getGeneralLedgerEntry();
249
250 if (ObjectUtils.isNotNull(glEntry)) {
251 // Add account amount to GL entry submit amount.
252 if (glEntry.getTransactionLedgerSubmitAmount() != null) {
253 glEntry.setTransactionLedgerSubmitAmount(glEntry.getTransactionLedgerSubmitAmount().add(account.getItemAccountTotalAmount()));
254 }
255 else {
256 glEntry.setTransactionLedgerSubmitAmount(new KualiDecimal(account.getItemAccountTotalAmount().toString()));
257 }
258 }
259 // add to the session for persistence
260 glEntryList.add(glEntry);
261 }
262 }
263
264
265 /**
266 * Build asset details/shared details/unique details lists for new asset global document
267 *
268 * @param selectedItem
269 * @param newDocument
270 * @param assetGlobal
271 */
272 protected void setAssetGlobalDetails(PurchasingAccountsPayableItemAsset selectedItem, AssetGlobal assetGlobal, Pretag preTag, PurchaseOrderCapitalAssetSystem capitalAssetSystem) {
273 // build assetGlobalDetail list( will be used for creating unique details list at the same time)
274 List<AssetGlobalDetail> assetDetailsList = assetGlobal.getAssetGlobalDetails();
275 // shared location details list
276 List<AssetGlobalDetail> sharedDetails = assetGlobal.getAssetSharedDetails();
277 for (int i = 0; i < selectedItem.getAccountsPayableItemQuantity().intValue(); i++) {
278 AssetGlobalDetail assetGlobalDetail = new AssetGlobalDetail();
279 assetGlobalDetail.setDocumentNumber(assetGlobal.getDocumentNumber());
280 assetGlobalDetail.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue());
281 assetDetailsList.add(assetGlobalDetail);
282 // build assetSharedDetails and assetGlobalUniqueDetails list. There two lists will be used to rebuild
283 // assetGlobalDetails list when AssetGlobalMaintainableImpl.prepareForSave() is called during save the document.
284 AssetGlobalDetail sharedDetail = new AssetGlobalDetail();
285 // added as unique detail
286 sharedDetail.getAssetGlobalUniqueDetails().add(assetGlobalDetail);
287 sharedDetails.add(sharedDetail);
288 }
289
290 // feeding data from pre-tag details into shared location details list and unique detail list
291 if (isItemPretagged(preTag)) {
292 setAssetDetailFromPreTag(preTag, sharedDetails, assetDetailsList);
293 }
294
295 // feeding location data from PurAp into assetGlobalDetail List.
296 if (!isItemFullyPretagged(preTag, assetGlobal) && ObjectUtils.isNotNull(capitalAssetSystem)) {
297 setAssetGlobalDetailFromPurAp(capitalAssetSystem, sharedDetails);
298 }
299 }
300
301 /**
302 * Check if the all new assets get pre-tagged by pre-tagging.
303 *
304 * @param preTag
305 * @param assetGlobal
306 * @return
307 */
308 protected boolean isItemFullyPretagged(Pretag preTag, AssetGlobal assetGlobal) {
309 if (isItemPretagged(preTag)) {
310 List<PretagDetail> pretagDetails = preTag.getPretagDetails();
311 int pretagSize = 0;
312 for (PretagDetail pretagDetail : pretagDetails) {
313 if (pretagDetail.isActive()) {
314 pretagSize++;
315 }
316 }
317 return pretagSize >= assetGlobal.getAssetSharedDetails().size();
318 }
319 return false;
320 }
321
322
323 /**
324 * Set asset global detail location information from PurAp input. In this method, no grouping for shared location because
325 * AssetGlobalMaintainableImpl.processAfterRetrieve() will group the shared location anyway...
326 *
327 * @param capitalAssetSystem
328 * @param assetDetailsList
329 */
330 protected void setAssetGlobalDetailFromPurAp(PurchaseOrderCapitalAssetSystem capitalAssetSystem, List<AssetGlobalDetail> assetSharedDetail) {
331 List<CapitalAssetLocation> capitalAssetLocations = capitalAssetSystem.getCapitalAssetLocations();
332
333 if (ObjectUtils.isNotNull(capitalAssetLocations) && !capitalAssetLocations.isEmpty()) {
334 Iterator<CapitalAssetLocation> locationIterator = capitalAssetLocations.iterator();
335 int locationQuantity = 0;
336 CapitalAssetLocation assetLocation = null;
337 for (AssetGlobalDetail assetDetail : assetSharedDetail) {
338 // if it's already pre-tagged, skip it.
339 if (StringUtils.isNotEmpty(assetDetail.getCampusCode())) {
340 continue;
341 }
342
343 // Each line item can have multiple locations and each location can have a quantity value with it.
344 if (locationQuantity <= 0 && locationIterator.hasNext()) {
345 // when we consume the current location quantity, we need to move to the next PurAp location.
346 assetLocation = locationIterator.next();
347 // initialize location quantity by PurAp setting
348 if (assetLocation.getItemQuantity() != null) {
349 locationQuantity = assetLocation.getItemQuantity().intValue();
350 }
351 else {
352 // if Purap not set item quantity, we set it to 1.
353 locationQuantity = 1;
354 }
355 }
356 else if (locationQuantity <= 0 && !locationIterator.hasNext()) {
357 // Consume the current location quantity and no more PurAp locations can be used, stop here.
358 break;
359 }
360 // set PurAp asset location into asset global document
361 setNewAssetByPurApLocation(assetLocation, assetDetail);
362
363 locationQuantity--;
364 }
365 }
366
367 }
368
369 /**
370 * Set asset global detail by PurAp asset location.
371 *
372 * @param assetLocation
373 * @param assetDetail
374 */
375 protected void setNewAssetByPurApLocation(CapitalAssetLocation assetLocation, AssetGlobalDetail assetDetail) {
376 String campusCode = assetLocation.getCampusCode();
377 // Set campus code only when it is a valid value. Otherwise, when save document, invalid data will violate data integrity
378 // and block save.
379 if (!StringUtils.isBlank(campusCode) && checkCampusCodeValid(campusCode)) {
380 assetDetail.setCampusCode(campusCode);
381
382 // for on-campus
383 if (!assetLocation.isOffCampusIndicator()) {
384 String buildingCode = assetLocation.getBuildingCode();
385 // Set building code only when it is a valid value. Otherwise, when save document, invalid data will violate data
386 // integrity and block save.
387 if (!StringUtils.isBlank(buildingCode) && checkBuildingCodeValid(campusCode, buildingCode)) {
388 assetDetail.setBuildingCode(buildingCode);
389
390 String buildingRoomNumber = assetLocation.getBuildingRoomNumber();
391 // Set building room number only when it is a valid value. Otherwise, when save document, invalid data will
392 // violate data integrity and block save.
393 if (!StringUtils.isBlank(buildingRoomNumber) && checkBuildingRoomNumberValid(campusCode, buildingCode, buildingRoomNumber)) {
394 assetDetail.setBuildingRoomNumber(buildingRoomNumber);
395 }
396 }
397 }
398 else {
399 // off-campus
400 assetDetail.setOffCampusCityName(assetLocation.getCapitalAssetCityName());
401 assetDetail.setOffCampusAddress(assetLocation.getCapitalAssetLine1Address());
402 assetDetail.setOffCampusCountryCode(assetLocation.getCapitalAssetCountryCode());
403 assetDetail.setOffCampusStateCode(assetLocation.getCapitalAssetStateCode());
404 assetDetail.setOffCampusZipCode(assetLocation.getCapitalAssetPostalCode());
405 }
406 }
407 }
408
409
410 /**
411 * Check the given buildingCode and campusCode valid.
412 *
413 * @param campusCode
414 * @param buildingCode
415 * @param buildingRoomNumber
416 * @return
417 */
418 protected boolean checkBuildingRoomNumberValid(String campusCode, String buildingCode, String buildingRoomNumber) {
419 Map<String, Object> pKeys = new HashMap<String, Object>();
420 pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAMPUS_CODE, campusCode);
421 pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.BUILDING_CODE, buildingCode);
422 pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.BUILDING_ROOM_NUMBER, buildingRoomNumber);
423 Room room = (Room) this.getBusinessObjectService().findByPrimaryKey(Room.class, pKeys);
424 return ObjectUtils.isNotNull(room) && room.isActive();
425 }
426
427
428 /**
429 * Check the given buildingCode and campusCode valid.
430 *
431 * @param buildingCode
432 * @return
433 */
434 protected boolean checkBuildingCodeValid(String campusCode, String buildingCode) {
435 Map<String, Object> pKeys = new HashMap<String, Object>();
436 pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAMPUS_CODE, campusCode);
437 pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.BUILDING_CODE, buildingCode);
438 Building building = (Building) this.getBusinessObjectService().findByPrimaryKey(Building.class, pKeys);
439 return ObjectUtils.isNotNull(building) && building.isActive();
440 }
441
442
443 /**
444 * check the given campus code existing and active status.
445 *
446 * @param campusCode
447 * @return
448 */
449 protected boolean checkCampusCodeValid(String campusCode) {
450 Map<String, Object> criteria = new HashMap<String, Object>();
451 criteria.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAMPUS_CODE, campusCode);
452 Campus campus = (Campus) SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(Campus.class).getExternalizableBusinessObject(Campus.class, criteria);
453 return ObjectUtils.isNotNull(campus) && campus.isActive();
454 }
455
456 /**
457 * Feeding data into assetGlobalDetail list from preTagDetail
458 *
459 * @param preTag
460 * @param assetDetailsList
461 */
462 protected void setAssetDetailFromPreTag(Pretag preTag, List<AssetGlobalDetail> assetSharedDetails, List<AssetGlobalDetail> assetUniqueDetails) {
463 Iterator<AssetGlobalDetail> sharedDetailsIterator = assetSharedDetails.iterator();
464 Iterator<AssetGlobalDetail> uniqueDetailsIterator = assetUniqueDetails.iterator();
465 for (PretagDetail preTagDetail : preTag.getPretagDetails()) {
466 if (!preTagDetail.isActive()) {
467 continue;
468 }
469 if (sharedDetailsIterator.hasNext()) {
470 // set shared location details
471 AssetGlobalDetail sharedDetail = sharedDetailsIterator.next();
472 sharedDetail.setBuildingCode(preTagDetail.getBuildingCode());
473 sharedDetail.setBuildingRoomNumber(preTagDetail.getBuildingRoomNumber());
474 sharedDetail.setBuildingSubRoomNumber(preTagDetail.getBuildingSubRoomNumber());
475 sharedDetail.setCampusCode(preTagDetail.getCampusCode());
476 // In-activate pre-tagging detail, and will be persistent to the DB.
477 preTagDetail.setActive(false);
478 }
479 if (uniqueDetailsIterator.hasNext()) {
480 // set asset unique detail
481 AssetGlobalDetail uniqueDetail = uniqueDetailsIterator.next();
482 uniqueDetail.setGovernmentTagNumber(preTagDetail.getGovernmentTagNumber());
483 uniqueDetail.setNationalStockNumber(preTagDetail.getNationalStockNumber());
484 uniqueDetail.setCampusTagNumber(preTagDetail.getCampusTagNumber());
485 uniqueDetail.setOrganizationInventoryName(preTag.getOrganizationInventoryName());
486 uniqueDetail.setSerialNumber(preTagDetail.getSerialNumber());
487 uniqueDetail.setRepresentativeUniversalIdentifier(preTag.getRepresentativeUniversalIdentifier());
488 // In-activate pre-tagging detail and will be persistent to the DB.
489 preTagDetail.setActive(false);
490 }
491 }
492 // In-activate preTag if possible.
493 inActivatePreTag(preTag);
494 }
495
496 /**
497 * In-activate preTag if all its preTagDetail entry are inactive.
498 *
499 * @param preTag
500 */
501 protected void inActivatePreTag(Pretag preTag) {
502 // get the number of inactive pre-tag detail.
503 int inActiveCounter = 0;
504 for (PretagDetail preTagDetail : preTag.getPretagDetails()) {
505 if (!preTagDetail.isActive()) {
506 inActiveCounter++;
507 }
508 }
509 // if the number of inactive preTagDetail is equal or greater than (when quantityInvoiced is decimal) quantityInvoiced,
510 // in-activate the preTag active field.
511 if (preTag.getQuantityInvoiced().isLessEqual(new KualiDecimal(inActiveCounter))) {
512 preTag.setActive(false);
513 }
514 }
515
516 /**
517 * Build asset payment details list for new asset global document.
518 *
519 * @param selectedItem
520 * @param assetGlobal
521 * @param requisitionIdentifier
522 */
523 protected void createAssetPaymentDetails(List<AssetPaymentDetail> assetPaymentList, PurchasingAccountsPayableItemAsset selectedItem, String documentNumber, Integer requisitionIdentifier) {
524 int seq = 1;
525
526 for (PurchasingAccountsPayableLineAssetAccount account : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
527 GeneralLedgerEntry glEntry = account.getGeneralLedgerEntry();
528
529 if (ObjectUtils.isNotNull(glEntry)) {
530 AssetPaymentDetail assetPaymentDetail = new AssetPaymentDetail();
531 // initialize payment detail fields
532 assetPaymentDetail.setDocumentNumber(documentNumber);
533 assetPaymentDetail.setSequenceNumber(Integer.valueOf(seq++));
534 assetPaymentDetail.setChartOfAccountsCode(glEntry.getChartOfAccountsCode());
535 assetPaymentDetail.setAccountNumber(replaceFiller(glEntry.getAccountNumber()));
536 assetPaymentDetail.setSubAccountNumber(replaceFiller(glEntry.getSubAccountNumber()));
537 assetPaymentDetail.setFinancialObjectCode(replaceFiller(glEntry.getFinancialObjectCode()));
538 assetPaymentDetail.setFinancialSubObjectCode(replaceFiller(glEntry.getFinancialSubObjectCode()));
539 assetPaymentDetail.setProjectCode(replaceFiller(glEntry.getProjectCode()));
540 assetPaymentDetail.setOrganizationReferenceId(glEntry.getOrganizationReferenceId());
541 assetPaymentDetail.setPostingYear(glEntry.getUniversityFiscalYear());
542 assetPaymentDetail.setPostingPeriodCode(glEntry.getUniversityFiscalPeriodCode());
543 assetPaymentDetail.setExpenditureFinancialSystemOriginationCode(replaceFiller(glEntry.getFinancialSystemOriginationCode()));
544 assetPaymentDetail.setExpenditureFinancialDocumentNumber(glEntry.getDocumentNumber());
545 assetPaymentDetail.setExpenditureFinancialDocumentTypeCode(replaceFiller(glEntry.getFinancialDocumentTypeCode()));
546 assetPaymentDetail.setExpenditureFinancialDocumentPostedDate(glEntry.getTransactionDate());
547 assetPaymentDetail.setAmount(account.getItemAccountTotalAmount());
548 assetPaymentDetail.setPurchaseOrderNumber(replaceFiller(glEntry.getReferenceFinancialDocumentNumber()));
549 assetPaymentDetail.setRequisitionNumber(requisitionIdentifier.toString());
550 assetPaymentDetail.setTransferPaymentIndicator(false);
551
552 // add to payment list
553 assetPaymentList.add(assetPaymentDetail);
554 }
555 }
556 }
557
558
559 /**
560 * In-activate item, item Account and generalLedgerEntry active indicator.
561 *
562 * @param selectedItem
563 * @param glEntryList
564 */
565 protected void inActivateItem(PurchasingAccountsPayableItemAsset selectedItem) {
566 // in-active each account.
567 for (PurchasingAccountsPayableLineAssetAccount selectedAccount : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
568 selectedAccount.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
569 }
570 // in-activate selected Item
571 selectedItem.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
572 }
573
574 /**
575 * Update GL Entry status as "enroute" if all its amount are consumed by submit CAMs document.Return the general ledger entry
576 * changes as a list.
577 *
578 * @param glEntryList
579 * @param selectedAccount
580 * @param glEntry
581 */
582 protected List<GeneralLedgerEntry> getGlEntryInActivedList(PurchasingAccountsPayableItemAsset selectedItem) {
583 List<GeneralLedgerEntry> glEntryUpdateList = new ArrayList<GeneralLedgerEntry>();
584 for (PurchasingAccountsPayableLineAssetAccount selectedAccount : selectedItem.getPurchasingAccountsPayableLineAssetAccounts()) {
585 GeneralLedgerEntry glEntry = selectedAccount.getGeneralLedgerEntry();
586 KualiDecimal relatedAccountAmount = KualiDecimal.ZERO;
587
588 // get persistent account list which should be save before hand
589 glEntry.refreshReferenceObject(CabPropertyConstants.GeneralLedgerEntry.PURAP_LINE_ASSET_ACCOUNTS);
590 // check if all accounts are inactive status
591 for (PurchasingAccountsPayableLineAssetAccount account : glEntry.getPurApLineAssetAccounts()) {
592 if (!account.isActive()) {
593 relatedAccountAmount = relatedAccountAmount.add(account.getItemAccountTotalAmount());
594 }
595 }
596
597 // if one account shows active, won't in-activate this general ledger entry.
598 if (relatedAccountAmount.compareTo(glEntry.getAmount()) == 0) {
599 glEntry.setActivityStatusCode(CabConstants.ActivityStatusCode.ENROUTE);
600 glEntryUpdateList.add(glEntry);
601 }
602 }
603 return glEntryUpdateList;
604 }
605
606
607 protected String replaceFiller(String val) {
608 return val == null ? "" : val.trim().replaceAll("-", "");
609 }
610
611 /**
612 * Create AssetGlobal BO and feed data from pre-asset tagging table.
613 *
614 * @param selectedItem
615 * @param newDocument
616 * @param preTag
617 * @return
618 */
619 protected AssetGlobal createAssetGlobal(PurchasingAccountsPayableItemAsset selectedItem, String documentNumber, Pretag preTag, Integer requisitionIdentifier) {
620 // instantiate AssetGlobal BO
621 AssetGlobal assetGlobal = new AssetGlobal();
622 assetGlobal.setDocumentNumber(documentNumber);
623 assetGlobal.setCapitalAssetDescription(selectedItem.getAccountsPayableLineItemDescription());
624 assetGlobal.setConditionCode(CamsConstants.Asset.CONDITION_CODE_E);
625 assetGlobal.setAcquisitionTypeCode(getAssetGlobalService().getNewAcquisitionTypeCode());
626 assetGlobal.setInventoryStatusCode(CamsConstants.InventoryStatusCode.CAPITAL_ASSET_ACTIVE_IDENTIFIABLE);
627 // set vendor name from Purchase Order Document
628 PurchaseOrderDocument purApdocument = this.getPurApInfoService().getCurrentDocumentForPurchaseOrderIdentifier(selectedItem.getPurchasingAccountsPayableDocument().getPurchaseOrderIdentifier());
629 if (purApdocument != null) {
630 assetGlobal.setVendorName(purApdocument.getVendorName());
631 }
632 // set origin code in the Asset Payment Document
633 assetGlobal.setCapitalAssetBuilderOriginIndicator(true);
634
635 PurchaseOrderCapitalAssetSystem capitalAssetSystem = null;
636 // check and set if purAp has new asset information
637 if (selectedItem.getCapitalAssetSystemIdentifier() != null) {
638 capitalAssetSystem = findCapitalAssetSystem(selectedItem.getCapitalAssetSystemIdentifier());
639 if (ObjectUtils.isNotNull(capitalAssetSystem)) {
640 setAssetGlobalFromPurAp(assetGlobal, capitalAssetSystem);
641 }
642 }
643 // feeding data from pre-asset tagging table into assetGlboal. Here, if there are data overlap in pretag and PurAp, we
644 // respect data in Pretagging.
645 if (isItemPretagged(preTag)) {
646 setAssetGlobalFromPreTag(preTag, assetGlobal);
647 }
648
649 // set asset global detail list
650 setAssetGlobalDetails(selectedItem, assetGlobal, preTag, capitalAssetSystem);
651
652 // Set Organization Inventory Name for each asset unique detail from PO
653 setOrgInventoryNameForAssetDetail(assetGlobal.getAssetGlobalDetails(), purApdocument);
654
655 // build payments list for asset global
656 createAssetPaymentDetails(assetGlobal.getAssetPaymentDetails(), selectedItem, documentNumber, requisitionIdentifier);
657
658 // set total cost
659 setAssetGlobalTotalCost(assetGlobal);
660 // Set Asset Global organization owner account, which is the account that contributed the most dollars.
661 setAssetGlobalOrgOwnerAccount(assetGlobal);
662
663 return assetGlobal;
664 }
665
666 /**
667 * Set organization inventory name for each asset detail by PO Contact name or if empty, by Requestor Name.
668 *
669 * @param assetGlobalDetails
670 * @param purApdocument
671 */
672 protected void setOrgInventoryNameForAssetDetail(List<AssetGlobalDetail> assetGlobalDetails, PurchaseOrderDocument purApdocument) {
673 if (ObjectUtils.isNotNull(purApdocument)) {
674 String orgInventoryName = purApdocument.getInstitutionContactName();
675
676 if (StringUtils.isBlank(orgInventoryName)) {
677 orgInventoryName = purApdocument.getRequestorPersonName();
678 }
679
680 for (AssetGlobalDetail assetGlobalDetail : assetGlobalDetails) {
681 assetGlobalDetail.setOrganizationInventoryName(orgInventoryName);
682 }
683 }
684 }
685
686
687 /**
688 * check if item is pre-tagged already.
689 *
690 * @param preTag
691 * @return
692 */
693 protected boolean isItemPretagged(Pretag preTag) {
694 return ObjectUtils.isNotNull(preTag) && preTag.isActive() && ObjectUtils.isNotNull(preTag.getPretagDetails()) && !preTag.getPretagDetails().isEmpty();
695 }
696
697
698 /**
699 * Set asset information from PurAp PurchaseOrderCapitalAssetSystem.
700 *
701 * @param assetGlobal
702 * @param capitalAssetSystem
703 */
704 protected void setAssetGlobalFromPurAp(AssetGlobal assetGlobal, PurchaseOrderCapitalAssetSystem capitalAssetSystem) {
705 assetGlobal.setManufacturerName(capitalAssetSystem.getCapitalAssetManufacturerName());
706 assetGlobal.setManufacturerModelNumber(capitalAssetSystem.getCapitalAssetModelDescription());
707 String capitalAssetTypeCode = capitalAssetSystem.getCapitalAssetTypeCode();
708 if (!StringUtils.isBlank(capitalAssetTypeCode) && checkCapitalAssetTypeCodeExist(capitalAssetTypeCode)) {
709 assetGlobal.setCapitalAssetTypeCode(capitalAssetSystem.getCapitalAssetTypeCode());
710 }
711 }
712
713
714 /**
715 * check the given capital asset type code exists in CAM
716 *
717 * @param capitalAssetTypeCode
718 * @return
719 */
720 protected boolean checkCapitalAssetTypeCodeExist(String capitalAssetTypeCode) {
721 Map<String, Object> pKeys = new HashMap<String, Object>();
722 pKeys.put(CabPropertyConstants.AssetGlobalDocumentCreate.CAPITAL_ASSET_TYPE_CODE, capitalAssetTypeCode);
723 AssetType assetType = (AssetType) this.getBusinessObjectService().findByPrimaryKey(AssetType.class, pKeys);
724 return ObjectUtils.isNotNull(assetType);
725 }
726
727
728 /**
729 * Get PurAp PurchaseOrderCapitalAssetSystem Object if exists.
730 *
731 * @param capitalAssetSystemIdentifier
732 * @return
733 */
734 protected PurchaseOrderCapitalAssetSystem findCapitalAssetSystem(Integer capitalAssetSystemIdentifier) {
735 Map pKeys = new HashMap<String, Object>();
736
737 pKeys.put(PurapPropertyConstants.CAPITAL_ASSET_SYSTEM_IDENTIFIER, capitalAssetSystemIdentifier);
738 return (PurchaseOrderCapitalAssetSystem) businessObjectService.findByPrimaryKey(PurchaseOrderCapitalAssetSystem.class, pKeys);
739 }
740
741
742 /**
743 * Set Asset Global org owner account and chart code. It's assigned by selecting the account that contributed the most dollars
744 * on the payment request.
745 *
746 * @param assetGlobal
747 */
748 protected void setAssetGlobalOrgOwnerAccount(AssetGlobal assetGlobal) {
749 AssetPaymentDetail maxCostPayment = null;
750 // get the maximum payment cost
751 for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) {
752 if (maxCostPayment == null || assetPaymentDetail.getAmount().isGreaterThan(maxCostPayment.getAmount())) {
753 maxCostPayment = assetPaymentDetail;
754 }
755 }
756
757 if (maxCostPayment != null) {
758 assetGlobal.setOrganizationOwnerAccountNumber(maxCostPayment.getAccountNumber());
759 assetGlobal.setOrganizationOwnerChartOfAccountsCode(maxCostPayment.getChartOfAccountsCode());
760 }
761 }
762
763
764 /**
765 * Set Asset Global total cost amount.
766 *
767 * @param assetGlobal
768 */
769 protected void setAssetGlobalTotalCost(AssetGlobal assetGlobal) {
770 KualiDecimal totalCost = KualiDecimal.ZERO;
771 for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) {
772 totalCost = totalCost.add(assetPaymentDetail.getAmount());
773 }
774
775 assetGlobal.setTotalCostAmount(totalCost);
776 }
777
778
779 /**
780 * Feeding data from preTag and set into asset global for shared information. PreTag data may override PurAp asset data since
781 * the strategy choose to respect Pretagging
782 *
783 * @param preTag
784 * @param assetGlobal
785 */
786 protected void setAssetGlobalFromPreTag(Pretag preTag, AssetGlobal assetGlobal) {
787 if (StringUtils.isNotBlank(preTag.getManufacturerName())) {
788 assetGlobal.setManufacturerName(preTag.getManufacturerName());
789 }
790 if (StringUtils.isNotBlank(preTag.getManufacturerModelNumber())) {
791 assetGlobal.setManufacturerModelNumber(preTag.getManufacturerModelNumber());
792 }
793
794 if (StringUtils.isNotBlank(preTag.getCapitalAssetTypeCode())) {
795 assetGlobal.setCapitalAssetTypeCode(preTag.getCapitalAssetTypeCode());
796 }
797 assetGlobal.setOrganizationText(preTag.getOrganizationText());
798 assetGlobal.setRepresentativeUniversalIdentifier(preTag.getRepresentativeUniversalIdentifier());
799 if (StringUtils.isNotBlank(preTag.getVendorName())) {
800 assetGlobal.setVendorName(preTag.getVendorName());
801 }
802 // acquisition type code is set to "P"(Pre-asset tagging)
803 assetGlobal.setAcquisitionTypeCode(CamsConstants.AssetGlobal.PRE_TAGGING_ACQUISITION_TYPE_CODE);
804 }
805
806 /**
807 * Gets the businessObjectService attribute.
808 *
809 * @return Returns the businessObjectService.
810 */
811 public BusinessObjectService getBusinessObjectService() {
812 return businessObjectService;
813 }
814
815 /**
816 * Sets the businessObjectService attribute value.
817 *
818 * @param businessObjectService The businessObjectService to set.
819 */
820 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
821 this.businessObjectService = businessObjectService;
822 }
823
824 /**
825 * Gets the documentService attribute.
826 *
827 * @return Returns the documentService.
828 */
829 public DocumentService getDocumentService() {
830 return documentService;
831 }
832
833
834 /**
835 * Sets the documentService attribute value.
836 *
837 * @param documentService The documentService to set.
838 */
839 public void setDocumentService(DocumentService documentService) {
840 this.documentService = documentService;
841 }
842
843 /**
844 * Gets the purApLineService attribute.
845 *
846 * @return Returns the purApLineService.
847 */
848 public PurApLineService getPurApLineService() {
849 return purApLineService;
850 }
851
852 /**
853 * Sets the purApLineService attribute value.
854 *
855 * @param purApLineService The purApLineService to set.
856 */
857 public void setPurApLineService(PurApLineService purApLineService) {
858 this.purApLineService = purApLineService;
859 }
860
861
862 /**
863 * Gets the purApInfoService attribute.
864 *
865 * @return Returns the purApInfoService.
866 */
867 public PurApInfoService getPurApInfoService() {
868 return purApInfoService;
869 }
870
871
872 /**
873 * Sets the purApInfoService attribute value.
874 *
875 * @param purApInfoService The purApInfoService to set.
876 */
877 public void setPurApInfoService(PurApInfoService purApInfoService) {
878 this.purApInfoService = purApInfoService;
879 }
880
881
882 private AssetGlobalService getAssetGlobalService() {
883 return assetGlobalService;
884 }
885
886
887 public void setAssetGlobalService(AssetGlobalService assetGlobalService) {
888 this.assetGlobalService = assetGlobalService;
889 }
890 }