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 }