001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.financial.document;
017:
018: import static org.kuali.test.util.KualiTestAssertionUtils.assertEquality;
019: import static org.kuali.test.util.KualiTestAssertionUtils.assertInequality;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: import junit.framework.Assert;
025:
026: import org.apache.commons.lang.StringUtils;
027: import org.apache.log4j.Logger;
028: import org.kuali.core.bo.AdHocRouteRecipient;
029: import org.kuali.core.bo.DocumentHeader;
030: import org.kuali.core.datadictionary.DataDictionary;
031: import org.kuali.core.datadictionary.TransactionalDocumentEntry;
032: import org.kuali.core.document.Copyable;
033: import org.kuali.core.document.Correctable;
034: import org.kuali.core.document.Document;
035: import org.kuali.core.exceptions.ValidationException;
036: import org.kuali.core.service.DataDictionaryService;
037: import org.kuali.core.service.DocumentService;
038: import org.kuali.core.service.TransactionalDocumentDictionaryService;
039: import org.kuali.core.util.GlobalVariables;
040: import org.kuali.core.util.ObjectUtils;
041: import org.kuali.kfs.KFSConstants;
042: import org.kuali.kfs.bo.SourceAccountingLine;
043: import org.kuali.kfs.bo.TargetAccountingLine;
044: import org.kuali.kfs.context.KualiTestBase;
045: import org.kuali.kfs.document.AccountingDocument;
046: import org.kuali.module.chart.bo.AccountingPeriod;
047: import org.kuali.module.chart.service.AccountingPeriodService;
048: import org.kuali.test.fixtures.UserNameFixture;
049: import org.kuali.test.monitor.ChangeMonitor;
050: import org.kuali.test.monitor.DocumentVersionMonitor;
051: import org.kuali.test.monitor.DocumentWorkflowStatusMonitor;
052: import org.kuali.workflow.WorkflowTestUtils;
053:
054: import edu.iu.uis.eden.exception.WorkflowException;
055:
056: public final class AccountingDocumentTestUtils extends KualiTestBase {
057: private static Logger LOG = Logger
058: .getLogger(AccountingDocumentTestUtils.class);
059:
060: public void testPlaceholder() {
061: assertTrue("Test needs to have at least one test.", true);
062: }
063:
064: public static void testAddAccountingLine(
065: AccountingDocument document,
066: List<SourceAccountingLine> sourceLines,
067: List<TargetAccountingLine> targetLines,
068: int expectedSourceTotal, int expectedTargetTotal)
069: throws Exception {
070: assertTrue("expected count should be > 0",
071: (expectedSourceTotal + expectedTargetTotal) > 0);
072: assertTrue("no lines found", (targetLines.size() + sourceLines
073: .size()) > 0);
074:
075: assertEquals(0, document.getSourceAccountingLines().size());
076: assertEquals(0, document.getTargetAccountingLines().size());
077:
078: // add source lines
079: for (SourceAccountingLine sourceLine : sourceLines) {
080: document.addSourceAccountingLine(sourceLine);
081: }
082: // add target lines
083: for (TargetAccountingLine targetLine : targetLines) {
084: document.addTargetAccountingLine(targetLine);
085: }
086:
087: assertEquals("source line count mismatch", expectedSourceTotal,
088: document.getSourceAccountingLines().size());
089: assertEquals("target line count mismatch", expectedTargetTotal,
090: document.getTargetAccountingLines().size());
091: }
092:
093: public static <T extends AccountingDocument> void testGetNewDocument_byDocumentClass(
094: Class<T> documentClass, DocumentService documentService)
095: throws Exception {
096: T document = (T) documentService.getNewDocument(documentClass);
097: // verify document was created
098: assertNotNull(document);
099: assertNotNull(document.getDocumentHeader());
100: assertNotNull(document.getDocumentHeader().getDocumentNumber());
101: }
102:
103: public static void testConvertIntoCopy_copyDisallowed(
104: AccountingDocument document,
105: DataDictionaryService dataDictionaryService)
106: throws Exception {
107: // change the dataDictionary to disallow copying
108: DataDictionary d = dataDictionaryService.getDataDictionary();
109: Class documentClass = document.getClass();
110: boolean originalValue = d.getDocumentEntry(
111: documentClass.getName()).getAllowsCopy();
112: try {
113: d.getDocumentEntry(documentClass.getName()).setAllowsCopy(
114: false);
115:
116: boolean failedAsExpected = false;
117: try {
118: ((Copyable) document).toCopy();
119: } catch (IllegalStateException e) {
120: failedAsExpected = true;
121: }
122:
123: assertTrue(failedAsExpected);
124: } finally {
125: d.getDocumentEntry(documentClass.getName()).setAllowsCopy(
126: originalValue);
127: }
128: }
129:
130: public static void testConvertIntoErrorCorrection_documentAlreadyCorrected(
131: AccountingDocument document,
132: TransactionalDocumentDictionaryService dictionaryService)
133: throws Exception {
134:
135: if (dictionaryService.getAllowsErrorCorrection(document)
136: .booleanValue()) {
137: DocumentHeader header = document.getDocumentHeader();
138: header.setCorrectedByDocumentId("1");
139:
140: boolean failedAsExpected = false;
141: try {
142: ((Correctable) document).toErrorCorrection();
143: } catch (IllegalStateException e) {
144: failedAsExpected = true;
145: }
146:
147: assertTrue(failedAsExpected);
148: }
149: }
150:
151: public static void testConvertIntoErrorCorrection_errorCorrectionDisallowed(
152: AccountingDocument document,
153: DataDictionaryService dataDictionaryService)
154: throws Exception {
155: // change the dataDictionary to disallow errorCorrection
156: DataDictionary d = dataDictionaryService.getDataDictionary();
157: Class documentClass = document.getClass();
158: boolean originalValue = ((TransactionalDocumentEntry) d
159: .getDocumentEntry(documentClass.getName()))
160: .getAllowsErrorCorrection();
161: try {
162: ((TransactionalDocumentEntry) d
163: .getDocumentEntry(documentClass.getName()))
164: .setAllowsErrorCorrection(false);
165:
166: boolean failedAsExpected = false;
167: try {
168: ((Correctable) document).toErrorCorrection();
169: } catch (IllegalStateException e) {
170: failedAsExpected = true;
171: }
172:
173: assertTrue(failedAsExpected);
174: } finally {
175: ((TransactionalDocumentEntry) d
176: .getDocumentEntry(documentClass.getName()))
177: .setAllowsErrorCorrection(originalValue);
178: }
179: }
180:
181: public static void testConvertIntoErrorCorrection_invalidYear(
182: AccountingDocument document,
183: TransactionalDocumentDictionaryService dictionaryService,
184: AccountingPeriodService accountingPeriodService)
185: throws Exception {
186: if (dictionaryService.getAllowsErrorCorrection(document)
187: .booleanValue()) {
188: // change to non-current posting year
189: Integer postingYear = document.getPostingYear();
190: AccountingPeriod accountingPeriod = accountingPeriodService
191: .getByPeriod(document.getAccountingPeriod()
192: .getUniversityFiscalPeriodCode(),
193: postingYear - 5);
194: assertNotNull("accounting period invalid for test",
195: accountingPeriod);
196: assertTrue(
197: "accounting period invalid (same as current year)",
198: postingYear != accountingPeriod
199: .getUniversityFiscalYear());
200: assertEquals(
201: "accounting period invalid. period codes must remain the same",
202: document.getAccountingPeriod()
203: .getUniversityFiscalPeriodCode(),
204: accountingPeriod.getUniversityFiscalPeriodCode());
205: document.setAccountingPeriod(accountingPeriod);
206:
207: boolean failedAsExpected = false;
208: try {
209: ((Correctable) document).toErrorCorrection();
210: fail("converted into error correction for an invalid year");
211: } catch (IllegalStateException e) {
212: failedAsExpected = true;
213: }
214: assertTrue(failedAsExpected);
215: }
216: }
217:
218: /**
219: * @ShouldCommitTransactions needed for this test
220: * @see ShouldCommitTransactions
221: */
222: public static void testRouteDocument(AccountingDocument document,
223: DocumentService documentService) throws Exception {
224: document.prepareForSave();
225:
226: assertFalse("R".equals(document.getDocumentHeader()
227: .getWorkflowDocument().getRouteHeader()
228: .getDocRouteStatus()));
229: routeDocument(document, "saving copy source document", null,
230: documentService);
231: DocumentWorkflowStatusMonitor am = new DocumentWorkflowStatusMonitor(
232: documentService, document.getDocumentNumber(), "R");
233: assertTrue(ChangeMonitor.waitUntilChange(am, 240, 5));
234: assertEquals("R", document.getDocumentHeader()
235: .getWorkflowDocument().getRouteHeader()
236: .getDocRouteStatus());
237: }
238:
239: /**
240: * @ShouldCommitTransactions needed for this test
241: * @see ShouldCommitTransactions
242: */
243:
244: public static void testConvertIntoErrorCorrection(
245: AccountingDocument document, int expectedPrePECount,
246: DocumentService documentService,
247: TransactionalDocumentDictionaryService dictionaryService)
248: throws Exception {
249: if (dictionaryService.getAllowsErrorCorrection(document)
250: .booleanValue()) {
251: String documentHeaderId = document.getDocumentNumber();
252: LOG.debug("documentHeaderId = " + documentHeaderId);
253: // route the original doc, wait for status change
254: routeDocument(document,
255: "saving errorCorrection source document", null,
256: documentService);
257: DocumentWorkflowStatusMonitor routeMonitor = new DocumentWorkflowStatusMonitor(
258: documentService, documentHeaderId, "R");
259: assertTrue(ChangeMonitor.waitUntilChange(routeMonitor, 240,
260: 5));
261: document = (AccountingDocument) documentService
262: .getByDocumentHeaderId(documentHeaderId);
263:
264: // mock a fully approved document
265: document.getDocumentHeader().getWorkflowDocument()
266: .getRouteHeader().setDocRouteStatus(
267: KFSConstants.DocumentStatusCodes.APPROVED);
268:
269: // collect some preCorrect data
270: String preCorrectId = document.getDocumentNumber();
271: String preCorrectCorrectsId = document.getDocumentHeader()
272: .getFinancialDocumentInErrorNumber();
273:
274: int preCorrectPECount = document
275: .getGeneralLedgerPendingEntries().size();
276: // int preCorrectNoteCount = document.getDocumentHeader().getNotes().size();
277:
278: List<? extends SourceAccountingLine> preCorrectSourceLines = (List<? extends SourceAccountingLine>) ObjectUtils
279: .deepCopy(new ArrayList(document
280: .getSourceAccountingLines()));
281: List<? extends TargetAccountingLine> preCorrectTargetLines = (List<? extends TargetAccountingLine>) ObjectUtils
282: .deepCopy(new ArrayList(document
283: .getTargetAccountingLines()));
284: // validate preCorrect state
285: assertNotNull(preCorrectId);
286: assertNull(preCorrectCorrectsId);
287:
288: assertEquals(expectedPrePECount, preCorrectPECount);
289: // assertEquals(0, preCorrectNoteCount);
290:
291: // do the error correction
292: ((Correctable) document).toErrorCorrection();
293: // compare to preCorrect state
294: String postCorrectId = document.getDocumentNumber();
295: LOG
296: .debug("postcorrect documentHeaderId = "
297: + postCorrectId);
298: assertFalse(postCorrectId.equals(preCorrectId));
299: // pending entries should be cleared
300: int postCorrectPECount = document
301: .getGeneralLedgerPendingEntries().size();
302: LOG.debug("postcorrect PE count = " + postCorrectPECount);
303: assertEquals(0, postCorrectPECount);
304: // TODO: revisit this is it still needed
305: // // count 1 note, compare to "correction" text
306: // int postCorrectNoteCount = document.getDocumentHeader().getNotes().size();
307: // assertEquals(1, postCorrectNoteCount);
308: // DocumentNote note = document.getDocumentHeader().getNote(0);
309: // LOG.debug("postcorrect note text = " + note.getFinancialDocumentNoteText());
310: // assertTrue(note.getFinancialDocumentNoteText().indexOf("correction") != -1);
311: // correctsId should be equal to old id
312: String correctsId = document.getDocumentHeader()
313: .getFinancialDocumentInErrorNumber();
314: LOG.debug("postcorrect correctsId = " + correctsId);
315: assertEquals(preCorrectId, correctsId);
316: // accounting lines should have sign reversed on amounts
317: List<SourceAccountingLine> postCorrectSourceLines = document
318: .getSourceAccountingLines();
319: assertEquals(preCorrectSourceLines.size(),
320: postCorrectSourceLines.size());
321: for (int i = 0; i < preCorrectSourceLines.size(); ++i) {
322: SourceAccountingLine preCorrectLine = preCorrectSourceLines
323: .get(i);
324: SourceAccountingLine postCorrectLine = postCorrectSourceLines
325: .get(i);
326:
327: LOG.debug("postcorrect line(docId,amount) = " + i + "("
328: + postCorrectId + ","
329: + postCorrectLine.getAmount());
330: assertEquality(postCorrectId, postCorrectLine
331: .getDocumentNumber());
332: assertEquality(preCorrectLine.getAmount().negated(),
333: postCorrectLine.getAmount());
334: }
335:
336: List<? extends TargetAccountingLine> postCorrectTargetLines = document
337: .getTargetAccountingLines();
338: assertEquals(preCorrectTargetLines.size(),
339: postCorrectTargetLines.size());
340: for (int i = 0; i < preCorrectTargetLines.size(); ++i) {
341: TargetAccountingLine preCorrectLine = preCorrectTargetLines
342: .get(i);
343: TargetAccountingLine postCorrectLine = postCorrectTargetLines
344: .get(i);
345:
346: LOG.debug("postcorrect line(docId,amount) = " + i + "("
347: + postCorrectId + ","
348: + postCorrectLine.getAmount());
349: assertEquality(postCorrectId, postCorrectLine
350: .getDocumentNumber());
351: assertEquality(preCorrectLine.getAmount().negated(),
352: postCorrectLine.getAmount());
353: }
354: }
355: }
356:
357: /**
358: * @ShouldCommitTransactions needed for this test
359: * @see ShouldCommitTransactions
360: */
361: public static void testSaveDocument(AccountingDocument document,
362: DocumentService documentService) throws Exception {
363: // get document parameter
364: document.prepareForSave();
365:
366: // save
367: saveDocument(document, documentService);
368:
369: // retrieve
370: AccountingDocument result = (AccountingDocument) documentService
371: .getByDocumentHeaderId(document.getDocumentNumber());
372:
373: // verify
374: assertMatch(document, result);
375: }
376:
377: /**
378: * @ShouldCommitTransactions needed for this test
379: * @see ShouldCommitTransactions
380: */
381: public static void testConvertIntoCopy(AccountingDocument document,
382: DocumentService documentService, int expectedPrePECount)
383: throws Exception {
384: // save the original doc, wait for status change
385: document.prepareForSave();
386: routeDocument(document, "saving copy source document", null,
387: documentService);
388: DocumentWorkflowStatusMonitor am = new DocumentWorkflowStatusMonitor(
389: documentService, document.getDocumentNumber(), "R");
390: assertTrue(ChangeMonitor.waitUntilChange(am, 240, 5));
391: // collect some preCopy data
392: String preCopyId = document.getDocumentNumber();
393: String preCopyCopiedFromId = document.getDocumentHeader()
394: .getFinancialDocumentTemplateNumber();
395:
396: int preCopyPECount = document.getGeneralLedgerPendingEntries()
397: .size();
398: // int preCopyNoteCount = document.getDocumentHeader().getNotes().size();
399: String preCopyStatus = document.getDocumentHeader()
400: .getWorkflowDocument().getRouteHeader()
401: .getDocRouteStatus();
402:
403: List<? extends SourceAccountingLine> preCopySourceLines = (List<? extends SourceAccountingLine>) ObjectUtils
404: .deepCopy((ArrayList) document
405: .getSourceAccountingLines());
406: List<? extends TargetAccountingLine> preCopyTargetLines = (List<? extends TargetAccountingLine>) ObjectUtils
407: .deepCopy((ArrayList) document
408: .getTargetAccountingLines());
409: // validate preCopy state
410: assertNotNull(preCopyId);
411: assertNull(preCopyCopiedFromId);
412:
413: assertEquals(expectedPrePECount, preCopyPECount);
414: // assertEquals(0, preCopyNoteCount);
415: assertEquals("R", preCopyStatus);
416: // do the copy
417: ((Copyable) document).toCopy();
418: // compare to preCopy state
419:
420: String postCopyId = document.getDocumentNumber();
421: assertFalse(postCopyId.equals(preCopyId));
422: // verify that docStatus has changed
423: String postCopyStatus = document.getDocumentHeader()
424: .getWorkflowDocument().getRouteHeader()
425: .getDocRouteStatus();
426: assertFalse(postCopyStatus.equals(preCopyStatus));
427: // pending entries should be cleared
428: int postCopyPECount = document.getGeneralLedgerPendingEntries()
429: .size();
430: assertEquals(0, postCopyPECount);
431:
432: // TODO: revisit this is it still needed
433: // count 1 note, compare to "copied" text
434: // int postCopyNoteCount = document.getDocumentHeader().getNotes().size();
435: // assertEquals(1, postCopyNoteCount);
436: // DocumentNote note = document.getDocumentHeader().getNote(0);
437: // assertTrue(note.getFinancialDocumentNoteText().indexOf("copied from") != -1);
438: // copiedFrom should be equal to old id
439: String copiedFromId = document.getDocumentHeader()
440: .getFinancialDocumentTemplateNumber();
441: assertEquals(preCopyId, copiedFromId);
442: // accounting lines should be have different docHeaderIds but same
443: // amounts
444: List<? extends SourceAccountingLine> postCopySourceLines = document
445: .getSourceAccountingLines();
446: assertEquals(preCopySourceLines.size(), postCopySourceLines
447: .size());
448: for (int i = 0; i < preCopySourceLines.size(); ++i) {
449: SourceAccountingLine preCopyLine = preCopySourceLines
450: .get(i);
451: SourceAccountingLine postCopyLine = postCopySourceLines
452: .get(i);
453:
454: assertInequality(preCopyLine.getDocumentNumber(),
455: postCopyLine.getDocumentNumber());
456: assertEquality(preCopyLine.getAmount(), postCopyLine
457: .getAmount());
458: }
459:
460: List<? extends TargetAccountingLine> postCopyTargetLines = document
461: .getTargetAccountingLines();
462: assertEquals(preCopyTargetLines.size(), postCopyTargetLines
463: .size());
464: for (int i = 0; i < preCopyTargetLines.size(); ++i) {
465: TargetAccountingLine preCopyLine = preCopyTargetLines
466: .get(i);
467: TargetAccountingLine postCopyLine = postCopyTargetLines
468: .get(i);
469:
470: assertInequality(preCopyLine.getDocumentNumber(),
471: postCopyLine.getDocumentNumber());
472: assertEquality(preCopyLine.getAmount(), postCopyLine
473: .getAmount());
474: }
475: }
476:
477: // helper methods
478: public static void routeDocument(AccountingDocument document,
479: String annotation,
480: List<AdHocRouteRecipient> adHocRoutingRecipients,
481: DocumentService documentService) throws WorkflowException {
482: try {
483: documentService.routeDocument(document, annotation,
484: adHocRoutingRecipients);
485: } catch (ValidationException e) {
486: // If the business rule evaluation fails then give us more info for debugging this test.
487: fail(e.getMessage() + ", " + GlobalVariables.getErrorMap());
488: }
489: }
490:
491: public static void approveDocument(AccountingDocument document,
492: DocumentService documentService) throws Exception {
493: Long initialVersion = document.getVersionNumber();
494: Long nextVersion = new Long(initialVersion.longValue() + 1);
495: documentService.approveDocument(document, "approving test doc",
496: null);
497:
498: DocumentVersionMonitor vm = new DocumentVersionMonitor(
499: documentService, document.getDocumentNumber(),
500: initialVersion);
501: assertTrue(ChangeMonitor.waitUntilChange(vm, 120, 10));
502: assertEquals(nextVersion, document.getVersionNumber());
503: }
504:
505: public static void routeDocument(AccountingDocument document,
506: DocumentService documentService) throws Exception {
507: final String STATUS = "R";
508:
509: assertFalse(STATUS.equals(document.getDocumentHeader()
510: .getWorkflowDocument().getRouteHeader()
511: .getDocRouteStatus()));
512: documentService.routeDocument(document, "routing test doc",
513: null);
514:
515: DocumentWorkflowStatusMonitor am = new DocumentWorkflowStatusMonitor(
516: documentService, document.getDocumentNumber(), STATUS);
517: assertTrue(ChangeMonitor.waitUntilChange(am, 120, 10));
518: assertEquals(STATUS, document.getDocumentHeader()
519: .getWorkflowDocument().getRouteHeader()
520: .getDocRouteStatus());
521: }
522:
523: public static void saveDocument(AccountingDocument document,
524: DocumentService documentService) throws WorkflowException {
525: try {
526: documentService.saveDocument(document);
527: } catch (ValidationException e) {
528: // If the business rule evaluation fails then give us more info for debugging this test.
529: fail(e.getMessage() + ", " + GlobalVariables.getErrorMap());
530: }
531: }
532:
533: public static void approve(String docHeaderId,
534: UserNameFixture user, String expectedNode,
535: DocumentService documentService) throws Exception {
536: WorkflowTestUtils.waitForApproveRequest(Long
537: .valueOf(docHeaderId), GlobalVariables.getUserSession()
538: .getUniversalUser());
539: Document document = documentService
540: .getByDocumentHeaderId(docHeaderId);
541: assertTrue(
542: "Document should be at routing node " + expectedNode,
543: WorkflowTestUtils.isAtNode(document, expectedNode));
544: assertTrue("Document should be enroute.", document
545: .getDocumentHeader().getWorkflowDocument()
546: .stateIsEnroute());
547: assertTrue(user + " should have an approve request.", document
548: .getDocumentHeader().getWorkflowDocument()
549: .isApprovalRequested());
550: documentService.approveDocument(document, "Test approving as "
551: + user, null);
552: }
553:
554: public static <T extends Document> void assertMatch(T document1,
555: T document2) {
556: Assert.assertEquals(document1.getDocumentNumber(), document2
557: .getDocumentNumber());
558: Assert.assertEquals(document1.getDocumentHeader()
559: .getWorkflowDocument().getDocumentType(), document2
560: .getDocumentHeader().getWorkflowDocument()
561: .getDocumentType());
562:
563: AccountingDocument d1 = (AccountingDocument) document1;
564: AccountingDocument d2 = (AccountingDocument) document2;
565: if (StringUtils.isNotBlank(d1.getPostingPeriodCode())
566: && StringUtils.isNotBlank(d2.getPostingPeriodCode())) {
567: // some documents just plain old don't store this b/c the GLPEs get generated with "getCurrentAccountingPeriod()"
568: Assert.assertEquals(d1.getPostingPeriodCode(), d2
569: .getPostingPeriodCode());
570: }
571: Assert.assertEquals(d1.getPostingYear(), d2.getPostingYear());
572: Assert.assertEquals(d1.getSourceAccountingLines().size(), d2
573: .getSourceAccountingLines().size());
574:
575: for (int i = 0; i < d1.getSourceAccountingLines().size(); i++) {
576: d1.getSourceAccountingLine(i).isLike(
577: d2.getSourceAccountingLine(i));
578: }
579: Assert.assertEquals(d1.getTargetAccountingLines().size(), d2
580: .getTargetAccountingLines().size());
581: for (int i = 0; i < d1.getTargetAccountingLines().size(); i++) {
582: d1.getTargetAccountingLine(i).isLike(
583: d2.getTargetAccountingLine(i));
584: }
585: }
586: }
|