/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.fp.document;
import static org.kuali.kfs.sys.document.AccountingDocumentTestUtils.approveDocument;
import static org.kuali.kfs.sys.document.AccountingDocumentTestUtils.routeDocument;
import static org.kuali.kfs.sys.document.AccountingDocumentTestUtils.testGetNewDocument_byDocumentClass;
import static org.kuali.kfs.sys.fixture.AccountingLineFixture.LINE2;
import static org.kuali.kfs.sys.fixture.AccountingLineFixture.LINE3;
import static org.kuali.kfs.sys.fixture.UserNameFixture.khuntley;
import static org.kuali.kfs.sys.fixture.UserNameFixture.rorenfro;
import java.util.ArrayList;
import java.util.List;
import org.kuali.kfs.sys.ConfigureContext;
import org.kuali.kfs.sys.DocumentTestUtils;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
import org.kuali.kfs.sys.businessobject.TargetAccountingLine;
import org.kuali.kfs.sys.context.KualiTestBase;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.AccountingDocument;
import org.kuali.kfs.sys.document.AccountingDocumentTestUtils;
import org.kuali.kfs.sys.fixture.AccountingLineFixture;
import org.kuali.kfs.sys.fixture.UserNameFixture;
import org.kuali.kfs.sys.suite.AnnotationTestSuite;
import org.kuali.kfs.sys.suite.CrossSectionSuite;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.kns.service.TransactionalDocumentDictionaryService;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.exception.DocumentAuthorizationException;
import org.kuali.rice.krad.exception.ValidationException;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.PersistenceService;
import org.kuali.rice.krad.util.GlobalVariables;
/**
* This class is used to test InternalBillingDocument.
*/
@ConfigureContext(session = khuntley)
public class InternalBillingDocumentTest extends KualiTestBase {
protected static final Class<InternalBillingDocument> DOCUMENT_CLASS = InternalBillingDocument.class;
protected Document getDocumentParameterFixture() throws Exception {
return DocumentTestUtils.createDocument(SpringContext.getBean(DocumentService.class), InternalBillingDocument.class);
}
protected List<AccountingLineFixture> getTargetAccountingLineParametersFromFixtures() {
List<AccountingLineFixture> list = new ArrayList<AccountingLineFixture>();
list.add(LINE2);
list.add(LINE3);
list.add(LINE2);
return list;
}
protected List<AccountingLineFixture> getSourceAccountingLineParametersFromFixtures() {
List<AccountingLineFixture> list = new ArrayList<AccountingLineFixture>();
list.add(LINE2);
list.add(LINE3);
list.add(LINE2);
return list;
}
protected int getExpectedPrePeCount() {
return 12;
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testApprove_addAccessibleAccount_ChangingTotals() throws Exception {
AccountingDocument retrieved;
AccountingDocument original;
String docId;
// switch user to WESPRICE, build and route document with
// accountingLines
changeCurrentUser(getInitialUserName());
original = buildDocument();
routeDocument(original, SpringContext.getBean(DocumentService.class));
docId = original.getDocumentNumber();
// switch user to another user, add accountingLines for accounts not
// controlled by this user
changeCurrentUser(getTestUserName());
retrieved = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId);
retrieved.addSourceAccountingLine(getSourceAccountingLineAccessibleAccount());
retrieved.addTargetAccountingLine(getTargetAccountingLineAccessibleAccount());
boolean failedAsExpected = false;
try {
approveDocument(retrieved, SpringContext.getBean(DocumentService.class));
}
catch (ValidationException e) {
failedAsExpected = true;
}
catch (DocumentAuthorizationException dae) {
// this means that the workflow status didn't change in time for the check, so this is
// an expected exception
failedAsExpected = true;
}
assertTrue("document should have failed validation: " + dumpMessageMapErrors(),failedAsExpected);
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testApprove_addInaccessibleAccount_sourceLine() throws Exception {
// switch user to WESPRICE, build and route document with
// accountingLines
AccountingDocument original;
AccountingDocument retrieved;
String docId;
changeCurrentUser(getInitialUserName());
original = buildDocument();
routeDocument(original, SpringContext.getBean(DocumentService.class));
docId = original.getDocumentNumber();
// switch user to AHORNICK, add sourceAccountingLine for account not controlled by this user
// (and add a balancing targetAccountingLine for an accessible account)
changeCurrentUser(getTestUserName());
retrieved = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId);
retrieved.addSourceAccountingLine(getSourceAccountingLineInaccessibleAccount());
retrieved.addTargetAccountingLine(getTargetAccountingLineAccessibleAccount());
// approve document, wait for failure b/c totals have changed
boolean failedAsExpected = false;
try {
approveDocument(retrieved, SpringContext.getBean(DocumentService.class));
}
catch (ValidationException e) {
failedAsExpected = true;
}
catch (DocumentAuthorizationException dae) {
// this means that the workflow status didn't change in time for the check, so this is
// an expected exception
failedAsExpected = true;
}
assertTrue("document should have failed validation: " + dumpMessageMapErrors(),failedAsExpected);
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testApprove_addInaccessibleAccount_targetLine() throws Exception {
AccountingDocument retrieved;
AccountingDocument original;
String docId;
// switch user to WESPRICE, build and route document with
// accountingLines
changeCurrentUser(getInitialUserName());
original = buildDocument();
routeDocument(original, SpringContext.getBean(DocumentService.class));
docId = original.getDocumentNumber();
// switch user to AHORNICK, add targetAccountingLine for accounts not
// controlled by this user
// (and add a balancing sourceAccountingLine for an accessible account)
changeCurrentUser(getTestUserName());
retrieved = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId);
retrieved.addTargetAccountingLine(getTargetAccountingLineInaccessibleAccount());
retrieved.addSourceAccountingLine(getSourceAccountingLineAccessibleAccount());
// approve document, wait for failure
boolean failedAsExpected = false;
try {
approveDocument(retrieved, SpringContext.getBean(DocumentService.class));
}
catch (ValidationException e) {
failedAsExpected = true;
}
catch (DocumentAuthorizationException dae) {
// this means that the workflow status didn't change in time for the check, so this is
// an expected exception
failedAsExpected = true;
}
assertTrue("document should have failed validation: " + dumpMessageMapErrors(),failedAsExpected);
}
/**
* This method...
* @throws Exception
*/
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testApprove_deleteAccessibleAccount() throws Exception {
// switch user to WESPRICE, build and route document with
// accountingLines
AccountingDocument retrieved;
AccountingDocument original;
String docId;
changeCurrentUser(getInitialUserName());
original = buildDocument();
routeDocument(original, SpringContext.getBean(DocumentService.class));
docId = original.getDocumentNumber();
// switch user to AHORNICK, delete sourceAccountingLine for accounts
// controlled by this user
// (and delete matching targetAccountingLine, for balance)
changeCurrentUser(getTestUserName());
retrieved = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId);
deleteSourceAccountingLine(retrieved, 0);
deleteTargetAccountingLine(retrieved, 0);
// approve document, wait for failure b/c totals have changed
boolean failedAsExpected = false;
try {
approveDocument(retrieved, SpringContext.getBean(DocumentService.class));
}
catch (ValidationException e) {
failedAsExpected = true;
}
catch (DocumentAuthorizationException dae) {
// this means that the workflow status didn't change in time for the check, so this is
// an expected exception
failedAsExpected = true;
}
assertTrue("document should have failed validation: " + dumpMessageMapErrors(),failedAsExpected);
}
@AnnotationTestSuite(CrossSectionSuite.class)
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testApprove_updateAccessibleAccount() throws Exception {
// switch user to WESPRICE, build and route document with
// accountingLines
AccountingDocument retrieved;
AccountingDocument original;
String docId;
changeCurrentUser(getInitialUserName());
original = buildDocument();
routeDocument(original, SpringContext.getBean(DocumentService.class));
docId = original.getDocumentNumber();
// switch user to RORENFRO, update sourceAccountingLine for accounts
// controlled by this user
// (and delete update targetAccountingLine, for balance)
changeCurrentUser(getTestUserName());
retrieved = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId);
// make sure totals don't change
KualiDecimal originalSourceLineAmt = retrieved.getSourceAccountingLine(0).getAmount();
KualiDecimal originalTargetLineAmt = retrieved.getTargetAccountingLine(0).getAmount();
KualiDecimal newSourceLineAmt = originalSourceLineAmt.divide(new KualiDecimal(2));
KualiDecimal newTargetLineAmt = originalTargetLineAmt.divide(new KualiDecimal(2));
// update existing lines with new amount which equals orig divided by 2
updateSourceAccountingLine(retrieved, 0, newSourceLineAmt.toString());
updateTargetAccountingLine(retrieved, 0, newTargetLineAmt.toString());
// add in new lines and change amounts
TargetAccountingLine newTargetLine = getTargetAccountingLineAccessibleAccount();
newTargetLine.setAmount(newTargetLineAmt);
SourceAccountingLine newSourceLine = getSourceAccountingLineAccessibleAccount();
newSourceLine.setAmount(newSourceLineAmt);
retrieved.addTargetAccountingLine(newTargetLine);
retrieved.addSourceAccountingLine(newSourceLine);
try {
approveDocument(retrieved, SpringContext.getBean(DocumentService.class));
}
catch (DocumentAuthorizationException dae) {
// this means that the workflow status didn't change in time for the check, so this is
// an expected exception
} catch ( ValidationException ex ) {
fail( "Business Rules Failed: " + dumpMessageMapErrors() );
}
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testApprove_updateInaccessibleAccount_sourceLine() throws Exception {
AccountingDocument retrieved;
AccountingDocument original;
String docId;
// switch user to WESPRICE, build and route document with
// accountingLines
changeCurrentUser(getInitialUserName());
original = buildDocument();
routeDocument(original, SpringContext.getBean(DocumentService.class));
docId = original.getDocumentNumber();
// switch user to AHORNICK, update sourceAccountingLines for accounts
// not controlled by this user
// (and update matching accessible targetLine, for balance)
changeCurrentUser(getTestUserName());
retrieved = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId);
updateSourceAccountingLine(retrieved, 1, "3.14");
updateTargetAccountingLine(retrieved, 0, "3.14");
// clear the OJB cache, otherwise when the original is retrieved later, it matches the updated doc and the
// system doesn't think an update occurred.
SpringContext.getBean(PersistenceService.class, "persistenceServiceOjb").clearCache();
// approve document, wait for failure
boolean failedAsExpected = false;
try {
approveDocument(retrieved, SpringContext.getBean(DocumentService.class));
}
catch (ValidationException e) {
failedAsExpected = GlobalVariables.getMessageMap().containsMessageKey(KFSKeyConstants.ERROR_ACCOUNTINGLINE_INACCESSIBLE_UPDATE);
}
assertTrue("document should have failed validation: " + dumpMessageMapErrors(),failedAsExpected);
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testApprove_updateInaccessibleAccount_targetLine() throws Exception {
// switch user to WESPRICE, build and route document with
// accountingLines
AccountingDocument retrieved;
AccountingDocument original;
String docId;
changeCurrentUser(getInitialUserName());
original = buildDocument();
routeDocument(original, SpringContext.getBean(DocumentService.class));
docId = original.getDocumentNumber();
// switch user to AHORNICK, update targetAccountingLine for accounts
// not controlled by this user
// (and update matching accessible sourceLine, for balance)
changeCurrentUser(getTestUserName());
retrieved = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId);
updateTargetAccountingLine(retrieved, 1, "2.81");
updateSourceAccountingLine(retrieved, 0, "2.81");
// clear the OJB cache, otherwise when the original is retrieved later, it matches the updated doc and the
// system doesn't think an update occurred.
SpringContext.getBean(PersistenceService.class, "persistenceServiceOjb").clearCache();
// approve document, wait for failure
boolean failedAsExpected = false;
try {
approveDocument(retrieved, SpringContext.getBean(DocumentService.class));
}
catch (ValidationException e) {
failedAsExpected = GlobalVariables.getMessageMap().containsMessageKey(KFSKeyConstants.ERROR_ACCOUNTINGLINE_INACCESSIBLE_UPDATE);
}
catch (DocumentAuthorizationException dae) {
// this means that the workflow status didn't change in time for the check, so this is
// an expected exception
failedAsExpected = true;
}
assertTrue( "Test did not fail as expected: " + dumpMessageMapErrors(), failedAsExpected);
}
public final void testAddAccountingLine() throws Exception {
List<SourceAccountingLine> sourceLines = generateSouceAccountingLines();
List<TargetAccountingLine> targetLines = generateTargetAccountingLines();
int expectedSourceTotal = sourceLines.size();
int expectedTargetTotal = targetLines.size();
AccountingDocumentTestUtils.testAddAccountingLine(DocumentTestUtils.createDocument(SpringContext.getBean(DocumentService.class), DOCUMENT_CLASS), sourceLines, targetLines, expectedSourceTotal, expectedTargetTotal);
}
public final void testGetNewDocument() throws Exception {
testGetNewDocument_byDocumentClass(DOCUMENT_CLASS, SpringContext.getBean(DocumentService.class));
}
public final void testConvertIntoCopy_copyDisallowed() throws Exception {
AccountingDocumentTestUtils.testConvertIntoCopy_copyDisallowed(buildDocument(), SpringContext.getBean(DataDictionaryService.class));
}
public final void testConvertIntoErrorCorrection_documentAlreadyCorrected() throws Exception {
AccountingDocumentTestUtils.testConvertIntoErrorCorrection_documentAlreadyCorrected(buildDocument(), SpringContext.getBean(TransactionalDocumentDictionaryService.class));
}
public final void testConvertIntoErrorCorrection_errorCorrectionDisallowed() throws Exception {
AccountingDocumentTestUtils.testConvertIntoErrorCorrection_errorCorrectionDisallowed(buildDocument(), SpringContext.getBean(DataDictionaryService.class));
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testConvertIntoErrorCorrection() throws Exception {
AccountingDocumentTestUtils.testConvertIntoErrorCorrection(buildDocument(), getExpectedPrePeCount(), SpringContext.getBean(DocumentService.class), SpringContext.getBean(TransactionalDocumentDictionaryService.class));
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testRouteDocument() throws Exception {
AccountingDocumentTestUtils.testRouteDocument(buildDocument(), SpringContext.getBean(DocumentService.class));
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testSaveDocument() throws Exception {
AccountingDocumentTestUtils.testSaveDocument(buildDocument(), SpringContext.getBean(DocumentService.class));
}
@ConfigureContext(session = khuntley, shouldCommitTransactions = true)
public final void testConvertIntoCopy() throws Exception {
AccountingDocumentTestUtils.testConvertIntoCopy(buildDocument(), SpringContext.getBean(DocumentService.class), getExpectedPrePeCount());
}
// test util methods
protected List<SourceAccountingLine> generateSouceAccountingLines() throws Exception {
List<SourceAccountingLine> sourceLines = new ArrayList<SourceAccountingLine>();
// set accountinglines to document
for (AccountingLineFixture sourceFixture : getSourceAccountingLineParametersFromFixtures()) {
sourceLines.add(sourceFixture.createSourceAccountingLine());
}
return sourceLines;
}
protected List<TargetAccountingLine> generateTargetAccountingLines() throws Exception {
List<TargetAccountingLine> targetLines = new ArrayList<TargetAccountingLine>();
for (AccountingLineFixture targetFixture : getTargetAccountingLineParametersFromFixtures()) {
targetLines.add(targetFixture.createTargetAccountingLine());
}
return targetLines;
}
protected InternalBillingDocument buildDocument() throws Exception {
// put accounting lines into document parameter for later
InternalBillingDocument document = (InternalBillingDocument) getDocumentParameterFixture();
// set accountinglines to document
for (AccountingLineFixture sourceFixture : getSourceAccountingLineParametersFromFixtures()) {
sourceFixture.addAsSourceTo(document);
}
for (AccountingLineFixture targetFixture : getTargetAccountingLineParametersFromFixtures()) {
targetFixture.addAsTargetTo(document);
}
return document;
}
protected void updateSourceAccountingLine(AccountingDocument document, int index, String newAmount) {
SourceAccountingLine sourceLine = document.getSourceAccountingLine(index);
sourceLine.setAmount(new KualiDecimal(newAmount));
}
protected void updateTargetAccountingLine(AccountingDocument document, int index, String newAmount) {
TargetAccountingLine targetLine = document.getTargetAccountingLine(index);
targetLine.setAmount(new KualiDecimal(newAmount));
}
protected void deleteSourceAccountingLine(AccountingDocument document, int index) {
List sourceLines = document.getSourceAccountingLines();
sourceLines.remove(index);
document.setSourceAccountingLines(sourceLines);
}
protected void deleteTargetAccountingLine(AccountingDocument document, int index) {
List targetLines = document.getTargetAccountingLines();
targetLines.remove(index);
document.setTargetAccountingLines(targetLines);
}
protected UserNameFixture getInitialUserName() {
return khuntley; // replace rjweiss with khuntley
}
protected UserNameFixture getTestUserName() {
return rorenfro;
}
protected SourceAccountingLine getSourceAccountingLineAccessibleAccount() throws Exception {
return LINE2.createSourceAccountingLine();
}
protected TargetAccountingLine getTargetAccountingLineInaccessibleAccount() throws Exception {
return LINE3.createTargetAccountingLine();
}
protected TargetAccountingLine getTargetAccountingLineAccessibleAccount() throws Exception {
return LINE2.createTargetAccountingLine();
}
protected SourceAccountingLine getSourceAccountingLineInaccessibleAccount() throws Exception {
return LINE3.createSourceAccountingLine();
}
}