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.fp.batch.service.impl; 017 018 import static org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants.AUTO_APPROVE_DOCUMENTS_IND; 019 import static org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants.AUTO_APPROVE_NUMBER_OF_DAYS; 020 import static org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants.DEFAULT_TRANS_ACCOUNT_PARM_NM; 021 import static org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants.DEFAULT_TRANS_CHART_CODE_PARM_NM; 022 import static org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants.DEFAULT_TRANS_OBJECT_CODE_PARM_NM; 023 import static org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants.ERROR_TRANS_ACCOUNT_PARM_NM; 024 import static org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants.SINGLE_TRANSACTION_IND_PARM_NM; 025 import static org.kuali.kfs.sys.KFSConstants.GL_CREDIT_CODE; 026 import static org.kuali.kfs.sys.KFSConstants.FinancialDocumentTypeCodes.PROCUREMENT_CARD; 027 028 import java.rmi.RemoteException; 029 import java.sql.Timestamp; 030 import java.util.ArrayList; 031 import java.util.HashMap; 032 import java.util.Iterator; 033 import java.util.List; 034 import java.util.Map; 035 036 import org.apache.commons.lang.StringUtils; 037 import org.kuali.kfs.fp.batch.ProcurementCardAutoApproveDocumentsStep; 038 import org.kuali.kfs.fp.batch.ProcurementCardCreateDocumentsStep; 039 import org.kuali.kfs.fp.batch.ProcurementCardLoadStep; 040 import org.kuali.kfs.fp.batch.service.ProcurementCardCreateDocumentService; 041 import org.kuali.kfs.fp.businessobject.ProcurementCardHolder; 042 import org.kuali.kfs.fp.businessobject.ProcurementCardSourceAccountingLine; 043 import org.kuali.kfs.fp.businessobject.ProcurementCardTargetAccountingLine; 044 import org.kuali.kfs.fp.businessobject.ProcurementCardTransaction; 045 import org.kuali.kfs.fp.businessobject.ProcurementCardTransactionDetail; 046 import org.kuali.kfs.fp.businessobject.ProcurementCardVendor; 047 import org.kuali.kfs.fp.document.ProcurementCardDocument; 048 import org.kuali.kfs.fp.document.validation.impl.ProcurementCardDocumentRuleConstants; 049 import org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService; 050 import org.kuali.kfs.sys.KFSConstants; 051 import org.kuali.kfs.sys.KFSPropertyConstants; 052 import org.kuali.kfs.sys.context.SpringContext; 053 import org.kuali.kfs.sys.document.service.AccountingLineRuleHelperService; 054 import org.kuali.kfs.sys.document.service.FinancialSystemDocumentService; 055 import org.kuali.kfs.sys.document.validation.event.DocumentSystemSaveEvent; 056 import org.kuali.rice.kew.dto.DocumentSearchCriteriaDTO; 057 import org.kuali.rice.kew.dto.DocumentSearchResultDTO; 058 import org.kuali.rice.kew.dto.DocumentSearchResultRowDTO; 059 import org.kuali.rice.kew.dto.KeyValueDTO; 060 import org.kuali.rice.kew.exception.WorkflowException; 061 import org.kuali.rice.kew.util.KEWConstants; 062 import org.kuali.rice.kew.util.KEWPropertyConstants; 063 import org.kuali.rice.kns.bo.DocumentHeader; 064 import org.kuali.rice.kns.service.BusinessObjectService; 065 import org.kuali.rice.kns.service.DataDictionaryService; 066 import org.kuali.rice.kns.service.DateTimeService; 067 import org.kuali.rice.kns.service.DocumentService; 068 import org.kuali.rice.kns.service.ParameterService; 069 import org.kuali.rice.kns.util.DateUtils; 070 import org.kuali.rice.kns.util.GlobalVariables; 071 import org.kuali.rice.kns.util.KualiDecimal; 072 import org.kuali.rice.kns.util.MessageMap; 073 import org.kuali.rice.kns.util.ObjectUtils; 074 import org.kuali.rice.kns.workflow.service.KualiWorkflowInfo; 075 import org.kuali.rice.kns.workflow.service.WorkflowDocumentService; 076 import org.springframework.transaction.annotation.Transactional; 077 078 079 /** 080 * This is the default implementation of the ProcurementCardCreateDocumentService interface. 081 * 082 * @see org.kuali.kfs.fp.batch.service.ProcurementCardCreateDocumentService 083 */ 084 @Transactional 085 public class ProcurementCardCreateDocumentServiceImpl implements ProcurementCardCreateDocumentService { 086 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ProcurementCardCreateDocumentServiceImpl.class); 087 088 protected static final String WORKFLOW_SEARCH_RESULT_KEY = KEWPropertyConstants.DOC_SEARCH_RESULT_PROPERTY_NAME_ROUTE_HEADER_ID; 089 090 protected ParameterService parameterService; 091 protected BusinessObjectService businessObjectService; 092 protected DocumentService documentService; 093 protected DataDictionaryService dataDictionaryService; 094 protected DateTimeService dateTimeService; 095 protected WorkflowDocumentService workflowDocumentService; 096 protected AccountingLineRuleHelperService accountingLineRuleUtil; 097 protected CapitalAssetBuilderModuleService capitalAssetBuilderModuleService; 098 099 100 /** 101 * This method retrieves a collection of credit card transactions and traverses through this list, creating 102 * ProcurementCardDocuments for each card. 103 * 104 * @return True if the procurement card documents were created successfully. If any problem occur while creating the 105 * documents, a runtime exception will be thrown. 106 * 107 * @see org.kuali.kfs.fp.batch.service.ProcurementCardCreateDocumentService#createProcurementCardDocuments() 108 */ 109 @SuppressWarnings("rawtypes") 110 public boolean createProcurementCardDocuments() { 111 List documents = new ArrayList(); 112 List cardTransactions = retrieveTransactions(); 113 114 // iterate through card transaction list and create documents 115 for (Iterator iter = cardTransactions.iterator(); iter.hasNext();) { 116 documents.add(createProcurementCardDocument((List) iter.next())); 117 } 118 119 // now store all the documents 120 for (Iterator iter = documents.iterator(); iter.hasNext();) { 121 ProcurementCardDocument pcardDocument = (ProcurementCardDocument) iter.next(); 122 try { 123 documentService.saveDocument(pcardDocument, DocumentSystemSaveEvent.class); 124 if ( LOG.isInfoEnabled() ) { 125 LOG.info("Saved Procurement Card document: "+pcardDocument.getDocumentNumber()); 126 } 127 } 128 catch (Exception e) { 129 LOG.error("Error persisting document # " + pcardDocument.getDocumentHeader().getDocumentNumber() + " " + e.getMessage(), e); 130 throw new RuntimeException("Error persisting document # " + pcardDocument.getDocumentHeader().getDocumentNumber() + " " + e.getMessage(), e); 131 } 132 } 133 134 return true; 135 } 136 137 /** 138 * This method retrieves all the procurement card documents with a status of 'I' and routes them to the next step in the 139 * routing path. 140 * 141 * @return True if the routing was performed successfully. A runtime exception will be thrown if any errors occur while routing. 142 * 143 * @see org.kuali.kfs.fp.batch.service.ProcurementCardCreateDocumentService#routeProcurementCardDocuments(java.util.List) 144 */ 145 public boolean routeProcurementCardDocuments() { 146 List<String> documentIdList = null; 147 try { 148 documentIdList = retrieveProcurementCardDocumentsToRoute(KEWConstants.ROUTE_HEADER_SAVED_CD); 149 } catch (WorkflowException e1) { 150 LOG.error("Error retrieving pcdo documents for routing: " + e1.getMessage(),e1); 151 throw new RuntimeException(e1.getMessage(),e1); 152 } catch (RemoteException re) { 153 LOG.error("Error retrieving pcdo documents for routing: " + re.getMessage(),re); 154 throw new RuntimeException(re.getMessage(),re); 155 } 156 157 //Collections.reverse(documentIdList); 158 if ( LOG.isInfoEnabled() ) { 159 LOG.info("PCards to Route: "+documentIdList); 160 } 161 162 for (String pcardDocumentId: documentIdList) { 163 try { 164 ProcurementCardDocument pcardDocument = (ProcurementCardDocument)documentService.getByDocumentHeaderId(pcardDocumentId); 165 if ( LOG.isInfoEnabled() ) { 166 LOG.info("Routing PCDO document # " + pcardDocumentId + "."); 167 } 168 documentService.prepareWorkflowDocument(pcardDocument); 169 170 // calling workflow service to bypass business rule checks 171 workflowDocumentService.route(pcardDocument.getDocumentHeader().getWorkflowDocument(), "", null); 172 } 173 catch (WorkflowException e) { 174 LOG.error("Error routing document # " + pcardDocumentId + " " + e.getMessage()); 175 throw new RuntimeException(e.getMessage(),e); 176 } 177 } 178 179 return true; 180 } 181 182 /** 183 * Returns a list of all initiated but not yet routed procurement card documents, using the KualiWorkflowInfo service. 184 * @return a list of procurement card documents to route 185 */ 186 protected List<String> retrieveProcurementCardDocumentsToRoute(String statusCode) throws WorkflowException, RemoteException { 187 List<String> documentIds = new ArrayList<String>(); 188 189 DocumentSearchCriteriaDTO criteria = new DocumentSearchCriteriaDTO(); 190 criteria.setDocTypeFullName(KFSConstants.FinancialDocumentTypeCodes.PROCUREMENT_CARD); 191 criteria.setDocRouteStatus(statusCode); 192 DocumentSearchResultDTO results = SpringContext.getBean(KualiWorkflowInfo.class).performDocumentSearch(GlobalVariables.getUserSession().getPerson().getPrincipalId(), criteria); 193 194 for (DocumentSearchResultRowDTO resultRow: results.getSearchResults()) { 195 for (KeyValueDTO field : resultRow.getFieldValues()) { 196 if (field.getKey().equals(WORKFLOW_SEARCH_RESULT_KEY)) { 197 documentIds.add(parseDocumentIdFromRouteDocHeader(field.getValue())); 198 } 199 } 200 } 201 202 return documentIds; 203 } 204 205 /** 206 * Retrieves the document id out of the route document header 207 * @param routeDocHeader the String representing an HTML link to the document 208 * @return the document id 209 */ 210 protected String parseDocumentIdFromRouteDocHeader(String routeDocHeader) { 211 int rightBound = routeDocHeader.indexOf('>') + 1; 212 int leftBound = routeDocHeader.indexOf('<', rightBound); 213 return routeDocHeader.substring(rightBound, leftBound); 214 } 215 216 /** 217 * This method determines if procurement card documents can be auto approved. A document can be auto approved if 218 * the grace period for allowing auto approval of a procurement card document has passed. The grace period is defined 219 * by a parameter in the parameters table. The create date of the document is then compared against the current date and 220 * if the difference is larger than the grace period defined, then the document is auto approved. 221 * 222 * @return This method always returns true. 223 * 224 * @see org.kuali.kfs.fp.batch.service.ProcurementCardCreateDocumentService#autoApproveProcurementCardDocuments() 225 */ 226 public boolean autoApproveProcurementCardDocuments() { 227 // check if auto approve is turned on 228 boolean autoApproveOn = parameterService.getIndicatorParameter(ProcurementCardAutoApproveDocumentsStep.class, AUTO_APPROVE_DOCUMENTS_IND); 229 230 if (!autoApproveOn) { // no auto approve? then skip out of here... 231 return true; 232 } 233 234 List<String> documentIdList = null; 235 try { 236 documentIdList = retrieveProcurementCardDocumentsToRoute(KEWConstants.ROUTE_HEADER_ENROUTE_CD); 237 } 238 catch (WorkflowException e1) { 239 throw new RuntimeException(e1.getMessage(),e1); 240 } 241 catch (RemoteException re) { 242 throw new RuntimeException(re.getMessage(),re); 243 } 244 245 // get number of days and type for auto approve 246 int autoApproveNumberDays = Integer.parseInt(parameterService.getParameterValue(ProcurementCardAutoApproveDocumentsStep.class, AUTO_APPROVE_NUMBER_OF_DAYS)); 247 248 Timestamp currentDate = dateTimeService.getCurrentTimestamp(); 249 for (String pcardDocumentId: documentIdList) { 250 try { 251 ProcurementCardDocument pcardDocument = (ProcurementCardDocument)documentService.getByDocumentHeaderId(pcardDocumentId); 252 253 // prevent PCard documents from auto approving if they have capital asset info to collect 254 if(capitalAssetBuilderModuleService.hasCapitalAssetObjectSubType(pcardDocument)) { 255 continue; 256 } 257 258 // if number of days in route is passed the allowed number, call doc service for super user approve 259 Timestamp docCreateDate = pcardDocument.getDocumentHeader().getWorkflowDocument().getCreateDate(); 260 if (DateUtils.getDifferenceInDays(docCreateDate, currentDate) > autoApproveNumberDays) { 261 // update document description to reflect the auto approval 262 pcardDocument.getDocumentHeader().setDocumentDescription("Auto Approved On " + dateTimeService.toDateTimeString(currentDate) + "."); 263 264 if ( LOG.isInfoEnabled() ) { 265 LOG.info("Auto approving document # " + pcardDocument.getDocumentHeader().getDocumentNumber()); 266 } 267 documentService.superUserApproveDocument(pcardDocument, ""); 268 } 269 } catch (WorkflowException e) { 270 LOG.error("Error auto approving document # " + pcardDocumentId + " " + e.getMessage(),e); 271 throw new RuntimeException(e.getMessage(),e); 272 } 273 } 274 275 return true; 276 } 277 278 279 /** 280 * This method retrieves a list of transactions from a temporary table, and groups them into document lists, based on 281 * single transaction indicator or a grouping by card. 282 * 283 * @return List containing transactions for document. 284 */ 285 @SuppressWarnings("rawtypes") 286 protected List retrieveTransactions() { 287 List groupedTransactions = new ArrayList(); 288 289 // retrieve records from transaction table order by card number 290 List transactions = (List) businessObjectService.findMatchingOrderBy(ProcurementCardTransaction.class, new HashMap(), KFSPropertyConstants.TRANSACTION_CREDIT_CARD_NUMBER, true); 291 292 // check apc for single transaction documents or multiple by card 293 boolean singleTransaction = parameterService.getIndicatorParameter(ProcurementCardCreateDocumentsStep.class, SINGLE_TRANSACTION_IND_PARM_NM); 294 295 List documentTransactions = new ArrayList(); 296 if (singleTransaction) { 297 for (Iterator iter = transactions.iterator(); iter.hasNext();) { 298 documentTransactions.add(iter.next()); 299 groupedTransactions.add(documentTransactions); 300 documentTransactions = new ArrayList(); 301 } 302 } 303 else { 304 Map cardTransactionsMap = new HashMap(); 305 for (Iterator iter = transactions.iterator(); iter.hasNext();) { 306 ProcurementCardTransaction transaction = (ProcurementCardTransaction) iter.next(); 307 if (!cardTransactionsMap.containsKey(transaction.getTransactionCreditCardNumber())) { 308 cardTransactionsMap.put(transaction.getTransactionCreditCardNumber(), new ArrayList()); 309 } 310 ((List) cardTransactionsMap.get(transaction.getTransactionCreditCardNumber())).add(transaction); 311 } 312 313 for (Iterator iter = cardTransactionsMap.values().iterator(); iter.hasNext();) { 314 groupedTransactions.add(iter.next()); 315 316 } 317 } 318 319 return groupedTransactions; 320 } 321 322 323 /** 324 * Creates a ProcurementCardDocument from the List of transactions given. 325 * 326 * @param transactions List of ProcurementCardTransaction objects to be used for creating the document. 327 * @return A ProcurementCardDocument populated with the transactions provided. 328 */ 329 protected ProcurementCardDocument createProcurementCardDocument(List transactions) { 330 ProcurementCardDocument pcardDocument = null; 331 332 try { 333 // get new document from doc service 334 pcardDocument = (ProcurementCardDocument) SpringContext.getBean(DocumentService.class).getNewDocument(PROCUREMENT_CARD); 335 if (ObjectUtils.isNotNull(pcardDocument.getCapitalAssetInformation())) { 336 pcardDocument.getCapitalAssetInformation().setDocumentNumber(pcardDocument.getDocumentNumber()); 337 } 338 339 // set the card holder record on the document from the first transaction 340 createCardHolderRecord(pcardDocument, (ProcurementCardTransaction) transactions.get(0)); 341 342 // for each transaction, create transaction detail object and then acct lines for the detail 343 int transactionLineNumber = 1; 344 KualiDecimal documentTotalAmount = KualiDecimal.ZERO; 345 String errorText = ""; 346 for (Iterator iter = transactions.iterator(); iter.hasNext();) { 347 ProcurementCardTransaction transaction = (ProcurementCardTransaction) iter.next(); 348 349 // create transaction detail record with accounting lines 350 errorText += createTransactionDetailRecord(pcardDocument, transaction, transactionLineNumber); 351 352 // update document total 353 documentTotalAmount = documentTotalAmount.add(transaction.getFinancialDocumentTotalAmount()); 354 355 transactionLineNumber++; 356 } 357 358 pcardDocument.getDocumentHeader().setFinancialDocumentTotalAmount(documentTotalAmount); 359 pcardDocument.getDocumentHeader().setDocumentDescription("SYSTEM Generated"); 360 361 // Remove duplicate messages from errorText 362 String messages[] = StringUtils.split(errorText, "."); 363 for (int i = 0; i < messages.length; i++) { 364 int countMatches = StringUtils.countMatches(errorText, messages[i]) - 1; 365 errorText = StringUtils.replace(errorText, messages[i] + ".", "", countMatches); 366 } 367 // In case errorText is still too long, truncate it and indicate so. 368 Integer documentExplanationMaxLength = dataDictionaryService.getAttributeMaxLength(DocumentHeader.class.getName(), KFSPropertyConstants.EXPLANATION); 369 if (documentExplanationMaxLength != null && errorText.length() > documentExplanationMaxLength.intValue()) { 370 String truncatedMessage = " ... TRUNCATED."; 371 errorText = errorText.substring(0, documentExplanationMaxLength - truncatedMessage.length()) + truncatedMessage; 372 } 373 pcardDocument.getDocumentHeader().setExplanation(errorText); 374 } 375 catch (WorkflowException e) { 376 LOG.error("Error creating pcdo documents: " + e.getMessage(),e); 377 throw new RuntimeException("Error creating pcdo documents: " + e.getMessage(),e); 378 } 379 380 return pcardDocument; 381 } 382 383 /** 384 * Creates card holder record and sets that record to the document given. 385 * 386 * @param pcardDocument Procurement card document to place the record in. 387 * @param transaction The transaction to set the card holder record fields from. 388 */ 389 protected void createCardHolderRecord(ProcurementCardDocument pcardDocument, ProcurementCardTransaction transaction) { 390 ProcurementCardHolder cardHolder = new ProcurementCardHolder(); 391 392 cardHolder.setDocumentNumber(pcardDocument.getDocumentNumber()); 393 cardHolder.setAccountNumber(transaction.getAccountNumber()); 394 cardHolder.setCardCycleAmountLimit(transaction.getCardCycleAmountLimit()); 395 cardHolder.setCardCycleVolumeLimit(transaction.getCardCycleVolumeLimit()); 396 cardHolder.setCardHolderAlternateName(transaction.getCardHolderAlternateName()); 397 cardHolder.setCardHolderCityName(transaction.getCardHolderCityName()); 398 cardHolder.setCardHolderLine1Address(transaction.getCardHolderLine1Address()); 399 cardHolder.setCardHolderLine2Address(transaction.getCardHolderLine2Address()); 400 cardHolder.setCardHolderName(transaction.getCardHolderName()); 401 cardHolder.setCardHolderStateCode(transaction.getCardHolderStateCode()); 402 cardHolder.setCardHolderWorkPhoneNumber(transaction.getCardHolderWorkPhoneNumber()); 403 cardHolder.setCardHolderZipCode(transaction.getCardHolderZipCode()); 404 cardHolder.setCardLimit(transaction.getCardLimit()); 405 cardHolder.setCardNoteText(transaction.getCardNoteText()); 406 cardHolder.setCardStatusCode(transaction.getCardStatusCode()); 407 cardHolder.setChartOfAccountsCode(transaction.getChartOfAccountsCode()); 408 cardHolder.setSubAccountNumber(transaction.getSubAccountNumber()); 409 cardHolder.setTransactionCreditCardNumber(transaction.getTransactionCreditCardNumber()); 410 411 pcardDocument.setProcurementCardHolder(cardHolder); 412 } 413 414 /** 415 * Creates a transaction detail record and adds that record to the document provided. 416 * 417 * @param pcardDocument Document to place record in. 418 * @param transaction Transaction to set fields from. 419 * @param transactionLineNumber Line number of the new transaction detail record within the procurement card document. 420 * @return The error text that was generated from the creation of the detail records. If the text is empty, no errors were encountered. 421 */ 422 protected String createTransactionDetailRecord(ProcurementCardDocument pcardDocument, ProcurementCardTransaction transaction, Integer transactionLineNumber) { 423 ProcurementCardTransactionDetail transactionDetail = new ProcurementCardTransactionDetail(); 424 425 // set the document transaction detail fields from the loaded transaction record 426 transactionDetail.setDocumentNumber(pcardDocument.getDocumentNumber()); 427 transactionDetail.setFinancialDocumentTransactionLineNumber(transactionLineNumber); 428 transactionDetail.setTransactionDate(transaction.getTransactionDate()); 429 transactionDetail.setTransactionReferenceNumber(transaction.getTransactionReferenceNumber()); 430 transactionDetail.setTransactionBillingCurrencyCode(transaction.getTransactionBillingCurrencyCode()); 431 transactionDetail.setTransactionCurrencyExchangeRate(transaction.getTransactionCurrencyExchangeRate()); 432 transactionDetail.setTransactionDate(transaction.getTransactionDate()); 433 transactionDetail.setTransactionOriginalCurrencyAmount(transaction.getTransactionOriginalCurrencyAmount()); 434 transactionDetail.setTransactionOriginalCurrencyCode(transaction.getTransactionOriginalCurrencyCode()); 435 transactionDetail.setTransactionPointOfSaleCode(transaction.getTransactionPointOfSaleCode()); 436 transactionDetail.setTransactionPostingDate(transaction.getTransactionPostingDate()); 437 transactionDetail.setTransactionPurchaseIdentifierDescription(transaction.getTransactionPurchaseIdentifierDescription()); 438 transactionDetail.setTransactionPurchaseIdentifierIndicator(transaction.getTransactionPurchaseIdentifierIndicator()); 439 transactionDetail.setTransactionSalesTaxAmount(transaction.getTransactionSalesTaxAmount()); 440 transactionDetail.setTransactionSettlementAmount(transaction.getTransactionSettlementAmount()); 441 transactionDetail.setTransactionTaxExemptIndicator(transaction.getTransactionTaxExemptIndicator()); 442 transactionDetail.setTransactionTravelAuthorizationCode(transaction.getTransactionTravelAuthorizationCode()); 443 transactionDetail.setTransactionUnitContactName(transaction.getTransactionUnitContactName()); 444 445 if (GL_CREDIT_CODE.equals(transaction.getTransactionDebitCreditCode())) { 446 transactionDetail.setTransactionTotalAmount(transaction.getFinancialDocumentTotalAmount().negated()); 447 } 448 else { 449 transactionDetail.setTransactionTotalAmount(transaction.getFinancialDocumentTotalAmount()); 450 } 451 452 // create transaction vendor record 453 createTransactionVendorRecord(pcardDocument, transaction, transactionDetail); 454 455 // add transaction detail to document 456 pcardDocument.getTransactionEntries().add(transactionDetail); 457 458 // now create the initial source and target lines for this transaction 459 return createAndValidateAccountingLines(pcardDocument, transaction, transactionDetail); 460 } 461 462 463 /** 464 * Creates a transaction vendor detail record and adds it to the transaction detail. 465 * 466 * @param pcardDocument The procurement card document to retrieve values from. 467 * @param transaction Transaction to set fields from. 468 * @param transactionDetail The transaction detail to set the vendor record on. 469 */ 470 protected void createTransactionVendorRecord(ProcurementCardDocument pcardDocument, ProcurementCardTransaction transaction, ProcurementCardTransactionDetail transactionDetail) { 471 ProcurementCardVendor transactionVendor = new ProcurementCardVendor(); 472 473 transactionVendor.setDocumentNumber(pcardDocument.getDocumentNumber()); 474 transactionVendor.setFinancialDocumentTransactionLineNumber(transactionDetail.getFinancialDocumentTransactionLineNumber()); 475 transactionVendor.setTransactionMerchantCategoryCode(transaction.getTransactionMerchantCategoryCode()); 476 transactionVendor.setVendorCityName(transaction.getVendorCityName()); 477 transactionVendor.setVendorLine1Address(transaction.getVendorLine1Address()); 478 transactionVendor.setVendorLine2Address(transaction.getVendorLine2Address()); 479 transactionVendor.setVendorName(transaction.getVendorName()); 480 transactionVendor.setVendorOrderNumber(transaction.getVendorOrderNumber()); 481 transactionVendor.setVendorStateCode(transaction.getVendorStateCode()); 482 transactionVendor.setVendorZipCode(transaction.getVendorZipCode()); 483 transactionVendor.setVisaVendorIdentifier(transaction.getVisaVendorIdentifier()); 484 485 transactionDetail.setProcurementCardVendor(transactionVendor); 486 } 487 488 /** 489 * From the transaction accounting attributes, creates source and target accounting lines. Attributes are validated first, and 490 * replaced with default and error values if needed. There will be 1 source and 1 target line generated. 491 * 492 * @param pcardDocument The procurement card document to add the new accounting lines to. 493 * @param transaction The transaction to process into account lines. 494 * @param docTransactionDetail The transaction detail to create source and target accounting lines from. 495 * @return String containing any error messages. 496 */ 497 protected String createAndValidateAccountingLines(ProcurementCardDocument pcardDocument, ProcurementCardTransaction transaction, ProcurementCardTransactionDetail docTransactionDetail) { 498 // build source lines 499 ProcurementCardSourceAccountingLine sourceLine = createSourceAccountingLine(transaction, docTransactionDetail); 500 sourceLine.setPostingYear(pcardDocument.getPostingYear()); 501 502 // add line to transaction through document since document contains the next sequence number fields 503 pcardDocument.addSourceAccountingLine(sourceLine); 504 505 // build target lines 506 ProcurementCardTargetAccountingLine targetLine = createTargetAccountingLine(transaction, docTransactionDetail); 507 targetLine.setPostingYear(pcardDocument.getPostingYear()); 508 509 // add line to transaction through document since document contains the next sequence number fields 510 pcardDocument.addTargetAccountingLine(targetLine); 511 512 return validateTargetAccountingLine(targetLine); 513 } 514 515 /** 516 * Creates the to record for the transaction. The chart of account attributes from the transaction are used to create 517 * the accounting line. 518 * 519 * @param transaction The transaction to pull information from to create the accounting line. 520 * @param docTransactionDetail The transaction detail to pull information from to populate the accounting line. 521 * @return The target accounting line fully populated with values from the parameters passed in. 522 */ 523 protected ProcurementCardTargetAccountingLine createTargetAccountingLine(ProcurementCardTransaction transaction, ProcurementCardTransactionDetail docTransactionDetail) { 524 ProcurementCardTargetAccountingLine targetLine = new ProcurementCardTargetAccountingLine(); 525 526 targetLine.setDocumentNumber(docTransactionDetail.getDocumentNumber()); 527 targetLine.setFinancialDocumentTransactionLineNumber(docTransactionDetail.getFinancialDocumentTransactionLineNumber()); 528 targetLine.setChartOfAccountsCode(transaction.getChartOfAccountsCode()); 529 targetLine.setAccountNumber(transaction.getAccountNumber()); 530 targetLine.setFinancialObjectCode(transaction.getFinancialObjectCode()); 531 targetLine.setSubAccountNumber(transaction.getSubAccountNumber()); 532 targetLine.setFinancialSubObjectCode(transaction.getFinancialSubObjectCode()); 533 targetLine.setProjectCode(transaction.getProjectCode()); 534 535 if (GL_CREDIT_CODE.equals(transaction.getTransactionDebitCreditCode())) { 536 targetLine.setAmount(transaction.getFinancialDocumentTotalAmount().negated()); 537 } 538 else { 539 targetLine.setAmount(transaction.getFinancialDocumentTotalAmount()); 540 } 541 542 return targetLine; 543 } 544 545 /** 546 * Creates the from record for the transaction. The clearing chart, account, and object code is used for creating the line. 547 * 548 * @param transaction The transaction to pull information from to create the accounting line. 549 * @param docTransactionDetail The transaction detail to pull information from to populate the accounting line. 550 * @return The source accounting line fully populated with values from the parameters passed in. 551 */ 552 protected ProcurementCardSourceAccountingLine createSourceAccountingLine(ProcurementCardTransaction transaction, ProcurementCardTransactionDetail docTransactionDetail) { 553 ProcurementCardSourceAccountingLine sourceLine = new ProcurementCardSourceAccountingLine(); 554 555 sourceLine.setDocumentNumber(docTransactionDetail.getDocumentNumber()); 556 sourceLine.setFinancialDocumentTransactionLineNumber(docTransactionDetail.getFinancialDocumentTransactionLineNumber()); 557 sourceLine.setChartOfAccountsCode(getDefaultChartCode()); 558 sourceLine.setAccountNumber(getDefaultAccountNumber()); 559 sourceLine.setFinancialObjectCode(getDefaultObjectCode()); 560 561 if (GL_CREDIT_CODE.equals(transaction.getTransactionDebitCreditCode())) { 562 sourceLine.setAmount(transaction.getFinancialDocumentTotalAmount().negated()); 563 } 564 else { 565 sourceLine.setAmount(transaction.getFinancialDocumentTotalAmount()); 566 } 567 568 return sourceLine; 569 } 570 571 /** 572 * Validates the chart of account attributes for existence and active indicator. Will substitute for defined 573 * default parameters or set fields to empty that if they have errors. 574 * 575 * @param targetLine The target accounting line to be validated. 576 * @return String with error messages discovered during validation. An empty string indicates no validation errors were found. 577 */ 578 protected String validateTargetAccountingLine(ProcurementCardTargetAccountingLine targetLine) { 579 String errorText = ""; 580 581 targetLine.refresh(); 582 583 if (!accountingLineRuleUtil.isValidChart(targetLine.getChart(), dataDictionaryService.getDataDictionary())) { 584 String tempErrorText = "Chart " + targetLine.getChartOfAccountsCode() + " is invalid; using error Chart Code."; 585 if ( LOG.isInfoEnabled() ) { 586 LOG.info(tempErrorText); 587 } 588 errorText += " " + tempErrorText; 589 590 targetLine.setChartOfAccountsCode(getErrorChartCode()); 591 targetLine.refresh(); 592 } 593 594 if (!accountingLineRuleUtil.isValidAccount(targetLine.getAccount(), dataDictionaryService.getDataDictionary()) || targetLine.getAccount().isExpired()) { 595 String tempErrorText = "Chart " + targetLine.getChartOfAccountsCode() + " Account " + targetLine.getAccountNumber() + " is invalid; using error account."; 596 if ( LOG.isInfoEnabled() ) { 597 LOG.info(tempErrorText); 598 } 599 errorText += " " + tempErrorText; 600 601 targetLine.setChartOfAccountsCode(getErrorChartCode()); 602 targetLine.setAccountNumber(getErrorAccountNumber()); 603 targetLine.refresh(); 604 } 605 606 if (!accountingLineRuleUtil.isValidObjectCode(targetLine.getObjectCode(), dataDictionaryService.getDataDictionary())) { 607 String tempErrorText = "Chart " + targetLine.getChartOfAccountsCode() + " Object Code " + targetLine.getFinancialObjectCode() + " is invalid; using default Object Code."; 608 if ( LOG.isInfoEnabled() ) { 609 LOG.info(tempErrorText); 610 } 611 errorText += " " + tempErrorText; 612 613 targetLine.setFinancialObjectCode(getDefaultObjectCode()); 614 targetLine.refresh(); 615 } 616 617 if (StringUtils.isNotBlank(targetLine.getSubAccountNumber()) && !accountingLineRuleUtil.isValidSubAccount(targetLine.getSubAccount(), dataDictionaryService.getDataDictionary())) { 618 String tempErrorText = "Chart " + targetLine.getChartOfAccountsCode() + " Account " + targetLine.getAccountNumber() + " Sub Account " + targetLine.getSubAccountNumber() + " is invalid; Setting Sub Account to blank."; 619 if ( LOG.isInfoEnabled() ) { 620 LOG.info(tempErrorText); 621 } 622 errorText += " " + tempErrorText; 623 624 targetLine.setSubAccountNumber(""); 625 } 626 627 if (StringUtils.isNotBlank(targetLine.getFinancialSubObjectCode()) && !accountingLineRuleUtil.isValidSubObjectCode(targetLine.getSubObjectCode(), dataDictionaryService.getDataDictionary())) { 628 String tempErrorText = "Chart " + targetLine.getChartOfAccountsCode() + " Account " + targetLine.getAccountNumber() + " Object Code " + targetLine.getFinancialObjectCode() + " Sub Object Code " + targetLine.getFinancialSubObjectCode() + " is invalid; setting Sub Object to blank."; 629 if ( LOG.isInfoEnabled() ) { 630 LOG.info(tempErrorText); 631 } 632 errorText += " " + tempErrorText; 633 634 targetLine.setFinancialSubObjectCode(""); 635 } 636 637 if (StringUtils.isNotBlank(targetLine.getProjectCode()) && !accountingLineRuleUtil.isValidProjectCode(targetLine.getProject(), dataDictionaryService.getDataDictionary())) { 638 if ( LOG.isInfoEnabled() ) { 639 LOG.info("Project Code " + targetLine.getProjectCode() + " is invalid; setting to blank."); 640 } 641 errorText += " Project Code " + targetLine.getProjectCode() + " is invalid; setting to blank."; 642 643 targetLine.setProjectCode(""); 644 } 645 646 // clear out GlobalVariable message map, since we have taken care of the errors 647 GlobalVariables.setMessageMap(new MessageMap()); 648 649 return errorText; 650 } 651 652 /** 653 * Retrieves the error chart code from the parameter table. 654 * @return The error chart code defined in the parameter table. 655 */ 656 protected String getErrorChartCode() { 657 return parameterService.getParameterValue(ProcurementCardCreateDocumentsStep.class, ProcurementCardDocumentRuleConstants.ERROR_TRANS_CHART_CODE_PARM_NM); 658 } 659 660 /** 661 * Retrieves the error account number from the parameter table. 662 * @return The error account number defined in the parameter table. 663 */ 664 protected String getErrorAccountNumber() { 665 return parameterService.getParameterValue(ProcurementCardCreateDocumentsStep.class, ERROR_TRANS_ACCOUNT_PARM_NM); 666 } 667 668 /** 669 * Retrieves the default chard code from the parameter table. 670 * @return The default chart code defined in the parameter table. 671 */ 672 protected String getDefaultChartCode() { 673 return parameterService.getParameterValue(ProcurementCardLoadStep.class, DEFAULT_TRANS_CHART_CODE_PARM_NM); 674 } 675 676 /** 677 * Retrieves the default account number from the parameter table. 678 * @return The default account number defined in the parameter table. 679 */ 680 protected String getDefaultAccountNumber() { 681 return parameterService.getParameterValue(ProcurementCardLoadStep.class, DEFAULT_TRANS_ACCOUNT_PARM_NM); 682 } 683 684 /** 685 * Retrieves the default object code from the parameter table. 686 * @return The default object code defined in the parameter table. 687 */ 688 protected String getDefaultObjectCode() { 689 return parameterService.getParameterValue(ProcurementCardLoadStep.class, DEFAULT_TRANS_OBJECT_CODE_PARM_NM); 690 } 691 692 /** 693 * Calls businessObjectService to remove all the procurement card transaction rows from the transaction load table. 694 */ 695 protected void cleanTransactionsTable() { 696 businessObjectService.deleteMatching(ProcurementCardTransaction.class, new HashMap()); 697 } 698 699 /** 700 * Loads all the parsed XML transactions into the temp transaction table. 701 * 702 * @param transactions List of ProcurementCardTransactions to load. 703 */ 704 protected void loadTransactions(List transactions) { 705 businessObjectService.save(transactions); 706 } 707 708 /** 709 * Sets the parameterService attribute. 710 * @param parameterService 711 */ 712 public void setParameterService(ParameterService parameterService) { 713 this.parameterService = parameterService; 714 } 715 716 /** 717 * Gets the businessObjectService attribute. 718 * @return Returns the businessObjectService. 719 */ 720 public BusinessObjectService getBusinessObjectService() { 721 return businessObjectService; 722 } 723 724 /** 725 * Sets the businessObjectService attribute. 726 * @param businessObjectService The businessObjectService to set. 727 */ 728 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 729 this.businessObjectService = businessObjectService; 730 } 731 732 /** 733 * Gets the documentService attribute. 734 * @return Returns the documentService. 735 */ 736 public DocumentService getDocumentService() { 737 return documentService; 738 } 739 740 /** 741 * Sets the documentService attribute. 742 * @param documentService The documentService to set. 743 */ 744 public void setDocumentService(DocumentService documentService) { 745 this.documentService = documentService; 746 } 747 748 749 /** 750 * Gets the dataDictionaryService attribute. 751 * @return Returns the dataDictionaryService. 752 */ 753 public DataDictionaryService getDataDictionaryService() { 754 return dataDictionaryService; 755 } 756 757 /** 758 * Sets the dataDictionaryService attribute. 759 * @param dataDictionaryService dataDictionaryService to set. 760 */ 761 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 762 this.dataDictionaryService = dataDictionaryService; 763 } 764 765 766 /** 767 * Gets the dateTimeService attribute. 768 * @return Returns the dateTimeService. 769 */ 770 public DateTimeService getDateTimeService() { 771 return dateTimeService; 772 } 773 774 /** 775 * Sets the dateTimeService attribute. 776 * @param dateTimeService The dateTimeService to set. 777 */ 778 public void setDateTimeService(DateTimeService dateTimeService) { 779 this.dateTimeService = dateTimeService; 780 } 781 782 /** 783 * Gets the workflowDocumentService attribute. 784 * @return Returns the workflowDocumentService. 785 */ 786 public WorkflowDocumentService getWorkflowDocumentService() { 787 return workflowDocumentService; 788 } 789 790 /** 791 * Sets the workflowDocumentService attribute value. 792 * @param workflowDocumentService The workflowDocumentService to set. 793 */ 794 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) { 795 this.workflowDocumentService = workflowDocumentService; 796 } 797 798 /** 799 * Sets the accountingLineRuleUtil attribute value. 800 * @param accountingLineRuleUtil The accountingLineRuleUtil to set. 801 */ 802 public void setAccountingLineRuleUtil(AccountingLineRuleHelperService accountingLineRuleUtil) { 803 this.accountingLineRuleUtil = accountingLineRuleUtil; 804 } 805 806 /** 807 * Sets the capitalAssetBuilderModuleService attribute value. 808 * @param capitalAssetBuilderModuleService The capitalAssetBuilderModuleService to set. 809 */ 810 public void setCapitalAssetBuilderModuleService(CapitalAssetBuilderModuleService capitalAssetBuilderModuleService) { 811 this.capitalAssetBuilderModuleService = capitalAssetBuilderModuleService; 812 } 813 814 }