/*
* 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.module.cab.document.service;
import java.util.ArrayList;
import java.util.List;
import org.kuali.kfs.module.cab.CabConstants;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableActionHistory;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableItemAsset;
import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableLineAssetAccount;
import org.kuali.kfs.module.cab.fixture.PurchasingAccountsPayableDocumentFixture;
import org.kuali.kfs.sys.ConfigureContext;
import org.kuali.kfs.sys.context.KualiTestBase;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.fixture.UserNameFixture;
import org.kuali.rice.core.api.util.type.KualiDecimal;
public class PurApLineServiceTest extends KualiTestBase {
private PurApLineService purApLineService;
private List<PurchasingAccountsPayableDocument> purApDocuments;
private PurchasingAccountsPayableItemAsset percentItemAsset;
private PurchasingAccountsPayableDocument preqDocumentWithSingleItemSingleAccount;
private PurchasingAccountsPayableDocument preqDocumentWithTwoItemsSingleAccount;
private PurchasingAccountsPayableDocument cmDocumentWithSingleItemTwoAccounts;
private String ERROR_MERGE_OBJECT_SUB_TYPES_DIFFERENT = "objectSubTypes are different for Merge";
private String ERROR_MERGE_OBJECT_SUB_TYPES_SAME = "objectSubTypes are the same for Merge";
private String ERROR_ALLOCATE_OBJECT_SUB_TYPES_DIFFERENT = "objectSubTypes are different for Allocate";
private String ERROR_ALLOCATE_OBJECT_SUB_TYPES_SAME = "objectSubTypes are the same for Allocate";
private String ERROR_PROCESS_SPLIT_SINGLE_ACCOUNT = "process split error happens when one item has one account";
private String ERROR_PROCESS_SPLIT_MULTIPLE_ACCOUNT = "process split error happens when one item has multiple accounts";
private String ERROR_PROCESS_MERGE = "process merge error";
private String ERROR_PROCESS_ALLOCATE = "process allocate error";
@ConfigureContext(session = UserNameFixture.khuntley, shouldCommitTransactions = false)
@Override
protected void setUp() throws Exception {
super.setUp();
purApLineService = SpringContext.getBean(PurApLineService.class);
prepareTestDataRecords();
}
private void prepareTestDataRecords() throws Exception {
// create 2 PREQ documents and 1 CM document matching the same PO. the first PREQ document has two line items and all the
// other document has one line item in each document. Each item has one accounting line associated with.
purApDocuments = PurchasingAccountsPayableDocumentFixture.createPurApDocuments();
preqDocumentWithTwoItemsSingleAccount = purApDocuments.get(0);
preqDocumentWithSingleItemSingleAccount = purApDocuments.get(1);
cmDocumentWithSingleItemTwoAccounts = purApDocuments.get(2);
percentItemAsset = preqDocumentWithTwoItemsSingleAccount.getPurchasingAccountsPayableItemAssets().get(0);
}
public void testProcessPercentPayment() throws Exception {
List<PurchasingAccountsPayableActionHistory> actionsTaken = new ArrayList<PurchasingAccountsPayableActionHistory>();
purApLineService.processPercentPayment(percentItemAsset, actionsTaken);
assertEquals(percentItemAsset.getAccountsPayableItemQuantity(), new KualiDecimal(1));
assertEquals(percentItemAsset.getTotalCost(), getTotalCost(percentItemAsset));
assertEquals(percentItemAsset.getUnitCost(), getTotalCost(percentItemAsset));
// check action taken history
assertEquals(actionsTaken.size(), 1);
assertEquals(actionsTaken.get(0).getActionTypeCode(), CabConstants.Actions.PERCENT_PAYMENT);
}
public void testProcessPercentPayment_noAction() throws Exception {
percentItemAsset.setAccountsPayableItemQuantity(new KualiDecimal(2));
List<PurchasingAccountsPayableActionHistory> actionsTaken = new ArrayList<PurchasingAccountsPayableActionHistory>();
purApLineService.processPercentPayment(percentItemAsset, actionsTaken);
assertEquals(percentItemAsset.getAccountsPayableItemQuantity(), new KualiDecimal(2));
// check action taken history
assertEquals(actionsTaken.size(), 0);
}
private KualiDecimal getTotalCost(PurchasingAccountsPayableItemAsset item) {
KualiDecimal totalCost = KualiDecimal.ZERO;
for (PurchasingAccountsPayableLineAssetAccount account : item.getPurchasingAccountsPayableLineAssetAccounts()) {
totalCost = totalCost.add(account.getItemAccountTotalAmount());
}
return totalCost;
}
public void testMergeLinesHasDifferentObjectSubTypes_True() {
List<PurchasingAccountsPayableItemAsset> items = new ArrayList<PurchasingAccountsPayableItemAsset>();
items.add(purApDocuments.get(0).getPurchasingAccountsPayableItemAssets().get(1));
items.add(purApDocuments.get(1).getPurchasingAccountsPayableItemAssets().get(0));
assertTrue(ERROR_MERGE_OBJECT_SUB_TYPES_DIFFERENT, purApLineService.mergeLinesHasDifferentObjectSubTypes(items));
}
public void testMergeLinesHasDifferentObjectSubTypes_False() {
List<PurchasingAccountsPayableItemAsset> items = new ArrayList<PurchasingAccountsPayableItemAsset>();
items.add(purApDocuments.get(0).getPurchasingAccountsPayableItemAssets().get(0));
items.add(purApDocuments.get(0).getPurchasingAccountsPayableItemAssets().get(1));
assertFalse(ERROR_MERGE_OBJECT_SUB_TYPES_SAME, purApLineService.mergeLinesHasDifferentObjectSubTypes(items));
}
public void testAllocateLinesHasDifferentObjectSubTypes_Ture() {
List<PurchasingAccountsPayableItemAsset> items = new ArrayList<PurchasingAccountsPayableItemAsset>();
items.add(purApDocuments.get(0).getPurchasingAccountsPayableItemAssets().get(0));
items.add(purApDocuments.get(0).getPurchasingAccountsPayableItemAssets().get(1));
PurchasingAccountsPayableItemAsset allocateSourceItem = purApDocuments.get(1).getPurchasingAccountsPayableItemAssets().get(0);
assertTrue(ERROR_ALLOCATE_OBJECT_SUB_TYPES_DIFFERENT, purApLineService.allocateLinesHasDifferentObjectSubTypes(items, allocateSourceItem));
}
public void testAllocateLinesHasDifferentObjectSubTypes_False() {
List<PurchasingAccountsPayableItemAsset> items = new ArrayList<PurchasingAccountsPayableItemAsset>();
items.add(purApDocuments.get(0).getPurchasingAccountsPayableItemAssets().get(0));
items.add(purApDocuments.get(2).getPurchasingAccountsPayableItemAssets().get(0));
PurchasingAccountsPayableItemAsset allocateSourceItem = purApDocuments.get(0).getPurchasingAccountsPayableItemAssets().get(1);
assertFalse(ERROR_ALLOCATE_OBJECT_SUB_TYPES_SAME, purApLineService.allocateLinesHasDifferentObjectSubTypes(items, allocateSourceItem));
}
public void testProcessSplit_ItemHasSingleAccount() {
List<PurchasingAccountsPayableActionHistory> actionsTaken = new ArrayList<PurchasingAccountsPayableActionHistory>();
PurchasingAccountsPayableItemAsset splitItemAsset = preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().get(0);
int oldItemSize = preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().size();
KualiDecimal oldQuantity = splitItemAsset.getAccountsPayableItemQuantity();
KualiDecimal oldTotalCost = getTotalCost(splitItemAsset);
splitItemAsset.setSplitQty(new KualiDecimal(1));
purApLineService.processSplit(splitItemAsset, actionsTaken);
// check new item created
assertTrue(ERROR_PROCESS_SPLIT_SINGLE_ACCOUNT, ++oldItemSize == preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().size());
// check split item new quantity
assertTrue(ERROR_PROCESS_SPLIT_SINGLE_ACCOUNT, splitItemAsset.getAccountsPayableItemQuantity().equals(oldQuantity.subtract(new KualiDecimal(1))));
// check the total quantity after split does not change
KualiDecimal newQuantity = KualiDecimal.ZERO;
for (PurchasingAccountsPayableItemAsset item : preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets()) {
newQuantity = newQuantity.add(item.getAccountsPayableItemQuantity());
}
assertEquals(oldQuantity, newQuantity);
// check the total cost after split does not change
KualiDecimal newTotalCost = KualiDecimal.ZERO;
newTotalCost = newTotalCost.add(getTotalCost(preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().get(0)));
newTotalCost = newTotalCost.add(getTotalCost(preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().get(1)));
assertTrue(ERROR_PROCESS_SPLIT_SINGLE_ACCOUNT, oldTotalCost.equals(newTotalCost));
// check the actionsTakenHistory has one entry for each account moved from source item to new
assertTrue(ERROR_PROCESS_SPLIT_SINGLE_ACCOUNT, actionsTaken.size() == 1);
}
public void testProcessSplit_ItemHasTwoAccounts() {
List<PurchasingAccountsPayableActionHistory> actionsTaken = new ArrayList<PurchasingAccountsPayableActionHistory>();
PurchasingAccountsPayableItemAsset splitItemAsset = cmDocumentWithSingleItemTwoAccounts.getPurchasingAccountsPayableItemAssets().get(0);
int oldItemSize = cmDocumentWithSingleItemTwoAccounts.getPurchasingAccountsPayableItemAssets().size();
KualiDecimal oldQuantity = splitItemAsset.getAccountsPayableItemQuantity();
KualiDecimal oldTotalCost = getTotalCost(splitItemAsset);
splitItemAsset.setSplitQty(new KualiDecimal(1));
purApLineService.processSplit(splitItemAsset, actionsTaken);
// check new item created
assertTrue(ERROR_PROCESS_SPLIT_MULTIPLE_ACCOUNT, ++oldItemSize == cmDocumentWithSingleItemTwoAccounts.getPurchasingAccountsPayableItemAssets().size());
// check split item new quantity
assertTrue(ERROR_PROCESS_SPLIT_MULTIPLE_ACCOUNT, splitItemAsset.getAccountsPayableItemQuantity().equals(oldQuantity.subtract(new KualiDecimal(1))));
// check the total quantity after split does not change
KualiDecimal newQuantity = KualiDecimal.ZERO;
for (PurchasingAccountsPayableItemAsset item : cmDocumentWithSingleItemTwoAccounts.getPurchasingAccountsPayableItemAssets()) {
newQuantity = newQuantity.add(item.getAccountsPayableItemQuantity());
}
assertEquals(oldQuantity, newQuantity);
// check the total cost after split does not change
KualiDecimal newTotalCost = KualiDecimal.ZERO;
newTotalCost = newTotalCost.add(getTotalCost(cmDocumentWithSingleItemTwoAccounts.getPurchasingAccountsPayableItemAssets().get(0)));
newTotalCost = newTotalCost.add(getTotalCost(cmDocumentWithSingleItemTwoAccounts.getPurchasingAccountsPayableItemAssets().get(1)));
assertTrue(ERROR_PROCESS_SPLIT_MULTIPLE_ACCOUNT, oldTotalCost.equals(newTotalCost));
// check the actionsTakenHistory has two entries since the source split item has two accounts.
assertTrue(ERROR_PROCESS_SPLIT_MULTIPLE_ACCOUNT, actionsTaken.size() == 2);
}
public void testProcessMerge_NotMergeAll() {
List<PurchasingAccountsPayableActionHistory> actionsTakeHistory = new ArrayList<PurchasingAccountsPayableActionHistory>();
List<PurchasingAccountsPayableItemAsset> mergeLines = new ArrayList<PurchasingAccountsPayableItemAsset>();
mergeLines.addAll(preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets());
mergeLines.addAll(preqDocumentWithTwoItemsSingleAccount.getPurchasingAccountsPayableItemAssets());
// calculate the total cost of all merge lines before action
KualiDecimal oldTotalCost = KualiDecimal.ZERO;
for (PurchasingAccountsPayableItemAsset item : mergeLines) {
oldTotalCost = oldTotalCost.add(getTotalCost(item));
}
purApLineService.processMerge(mergeLines, actionsTakeHistory, false);
// check all lines merge into the first item.
assertTrue(ERROR_PROCESS_MERGE, preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().size() == 1);
assertTrue(ERROR_PROCESS_MERGE, preqDocumentWithTwoItemsSingleAccount.getPurchasingAccountsPayableItemAssets().isEmpty());
// check the total cost after merge does not change
KualiDecimal newTotalCost = getTotalCost(preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().get(0));
assertTrue(ERROR_PROCESS_MERGE, oldTotalCost.equals(newTotalCost));
// check document is inactive since all its items are merged into another document item
assertTrue(ERROR_PROCESS_MERGE, !preqDocumentWithTwoItemsSingleAccount.isActive());
// check the actionsTakeHistory
assertTrue(ERROR_PROCESS_MERGE, actionsTakeHistory.size() == 2);
}
public void testProcessAllocate_AllocateOneItemToAllTheOtherItems() {
List<PurchasingAccountsPayableActionHistory> actionsTakeHistory = new ArrayList<PurchasingAccountsPayableActionHistory>();
PurchasingAccountsPayableItemAsset sourceLineItem = preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().get(0);
List<PurchasingAccountsPayableItemAsset> allocateTargetLines = new ArrayList<PurchasingAccountsPayableItemAsset>();
// preserve the total cost before allocate.
KualiDecimal oldTotalCost = getTotalCost(sourceLineItem);
// build the allocate target item list
for (PurchasingAccountsPayableItemAsset item : preqDocumentWithTwoItemsSingleAccount.getPurchasingAccountsPayableItemAssets()) {
allocateTargetLines.add(item);
oldTotalCost = oldTotalCost.add(getTotalCost(item));
}
for (PurchasingAccountsPayableItemAsset item : cmDocumentWithSingleItemTwoAccounts.getPurchasingAccountsPayableItemAssets()) {
allocateTargetLines.add(item);
oldTotalCost = oldTotalCost.add(getTotalCost(item));
}
purApLineService.processAllocate(sourceLineItem, allocateTargetLines, actionsTakeHistory, purApDocuments, false);
// check the source item is removed from the document
assertTrue("PurchasingAccountsPayableItemAssets was not empty", preqDocumentWithSingleItemSingleAccount.getPurchasingAccountsPayableItemAssets().isEmpty());
// check the source is inactive
assertFalse("preqDocumentWithSingleItemSingleAccount was active", preqDocumentWithSingleItemSingleAccount.isActive());
// check total amount of all documents doesn't change after allocate
KualiDecimal newTotalCost = KualiDecimal.ZERO;
for (PurchasingAccountsPayableItemAsset item : allocateTargetLines) {
newTotalCost = newTotalCost.add(getTotalCost(item));
}
assertTrue(ERROR_PROCESS_ALLOCATE, oldTotalCost.equals(newTotalCost));
// check the actionTakenHistory, allocate based on target account amount. so the number of actions taken equals the target accounts.
assertTrue(ERROR_PROCESS_ALLOCATE, actionsTakeHistory.size() == 4);
}
}