/*
* 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.ar.document;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAward;
import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAwardAccount;
import org.kuali.kfs.integration.cg.ContractsAndGrantsLetterOfCreditFund;
import org.kuali.kfs.integration.cg.ContractsAndGrantsLetterOfCreditFundGroup;
import org.kuali.kfs.module.ar.ArConstants;
import org.kuali.kfs.module.ar.ArKeyConstants;
import org.kuali.kfs.module.ar.ArPropertyConstants;
import org.kuali.kfs.module.ar.businessobject.ContractsGrantsInvoiceDocumentErrorLog;
import org.kuali.kfs.module.ar.businessobject.ContractsGrantsLetterOfCreditReviewDetail;
import org.kuali.kfs.module.ar.document.service.ContractsGrantsInvoiceDocumentService;
import org.kuali.kfs.module.ar.document.service.ContractsGrantsLetterOfCreditReviewDocumentService;
import org.kuali.kfs.module.ar.service.ContractsGrantsInvoiceCreateDocumentService;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.SystemOptions;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
import org.kuali.rice.krad.service.KualiModuleService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Contracts & Grants LOC Review Document.
*/
public class ContractsGrantsLetterOfCreditReviewDocument extends FinancialSystemTransactionalDocumentBase {
private static final Logger LOG = Logger.getLogger(ContractsGrantsLetterOfCreditReviewDocument.class);
private String letterOfCreditFundCode;
private ContractsAndGrantsLetterOfCreditFund letterOfCreditFund;
private String letterOfCreditFundGroupCode;
private ContractsAndGrantsLetterOfCreditFundGroup letterOfCreditFundGroup;
private List<ContractsGrantsLetterOfCreditReviewDetail> headerReviewDetails;
private List<ContractsGrantsLetterOfCreditReviewDetail> accountReviewDetails;
private transient static volatile ContractsGrantsLetterOfCreditReviewDocumentService contractsGrantsLetterOfCreditReviewDocumentService;
private transient static volatile OptionsService optionsService;
public ContractsGrantsLetterOfCreditReviewDocument() {
headerReviewDetails = new ArrayList<ContractsGrantsLetterOfCreditReviewDetail>();
accountReviewDetails = new ArrayList<ContractsGrantsLetterOfCreditReviewDetail>();
}
/**
* Gets the letterOfCreditFundGroupCode attribute.
*
* @return Returns the letterOfCreditFundGroupCode.
*/
public String getLetterOfCreditFundGroupCode() {
return letterOfCreditFundGroupCode;
}
/**
* Sets the letterOfCreditFundGroupCode attribute value.
*
* @param letterOfCreditFundGroupCode The letterOfCreditFundGroupCode to set.
*/
public void setLetterOfCreditFundGroupCode(String letterOfCreditFundGroupCode) {
this.letterOfCreditFundGroupCode = letterOfCreditFundGroupCode;
}
/**
* Gets the letterOfCreditFundCode attribute.
*
* @return Returns the letterOfCreditFundCode.
*/
public String getLetterOfCreditFundCode() {
return letterOfCreditFundCode;
}
/**
* Sets the letterOfCreditFundCode attribute value.
*
* @param letterOfCreditFundCode The letterOfCreditFundCode to set.
*/
public void setLetterOfCreditFundCode(String letterOfCreditFundCode) {
this.letterOfCreditFundCode = letterOfCreditFundCode;
}
/**
* Gets the letterOfCreditFund attribute.
*
* @return Returns the letterOfCreditFund.
*/
public ContractsAndGrantsLetterOfCreditFund getLetterOfCreditFund() {
return letterOfCreditFund = SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(ContractsAndGrantsLetterOfCreditFund.class).retrieveExternalizableBusinessObjectIfNecessary(this, letterOfCreditFund, ArPropertyConstants.LETTER_OF_CREDIT_FUND);
}
/**
* Sets the letterOfCreditFund attribute value.
*
* @param letterOfCreditFund The letterOfCreditFund to set.
*/
public void setLetterOfCreditFund(ContractsAndGrantsLetterOfCreditFund letterOfCreditFund) {
this.letterOfCreditFund = letterOfCreditFund;
}
/**
* Gets the letterOfCreditFundGroup attribute.
*
* @return Returns the letterOfCreditFundGroup.
*/
public ContractsAndGrantsLetterOfCreditFundGroup getLetterOfCreditFundGroup() {
return letterOfCreditFundGroup = SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(ContractsAndGrantsLetterOfCreditFundGroup.class).retrieveExternalizableBusinessObjectIfNecessary(this, letterOfCreditFundGroup, ArPropertyConstants.LETTER_OF_CREDIT_FUND_GROUP);
}
/**
* Sets the letterOfCreditFundGroup attribute value.
*
* @param letterOfCreditFundGroup The letterOfCreditFundGroup to set.
*/
public void setLetterOfCreditFundGroup(ContractsAndGrantsLetterOfCreditFundGroup letterOfCreditFundGroup) {
this.letterOfCreditFundGroup = letterOfCreditFundGroup;
}
/**
* Gets the CcaReviewDetails attribute.
*
* @return Returns the CcaReviewDetails.
*/
public List<ContractsGrantsLetterOfCreditReviewDetail> getHeaderReviewDetails() {
// To get the list of invoice Details for total cost
List<ContractsGrantsLetterOfCreditReviewDetail> hdrDetails = new ArrayList<ContractsGrantsLetterOfCreditReviewDetail>();
for (ContractsGrantsLetterOfCreditReviewDetail hdrD : headerReviewDetails) {
if (ObjectUtils.isNull(hdrD.getAccountDescription())) {
hdrDetails.add(hdrD);
}
}
return hdrDetails;
}
/**
* Sets the CcaReviewDetails attribute value.
*
* @param CcaReviewDetails The CcaReviewDetails to set.
*/
public void setHeaderReviewDetails(List<ContractsGrantsLetterOfCreditReviewDetail> headerReviewDetails) {
this.headerReviewDetails = headerReviewDetails;
}
/**
* Gets the AccountReviewDetails attribute.
*
* @return Returns the AccountReviewDetails.
*/
public List<ContractsGrantsLetterOfCreditReviewDetail> getAccountReviewDetails() {
// To get the list of invoice Details for total cost
List<ContractsGrantsLetterOfCreditReviewDetail> acctDetails = new ArrayList<ContractsGrantsLetterOfCreditReviewDetail>();
for (ContractsGrantsLetterOfCreditReviewDetail acctD : accountReviewDetails) {
if (ObjectUtils.isNotNull(acctD.getAccountDescription())) {
if (acctD.getAccountDescription().equalsIgnoreCase(ArConstants.ACCOUNT) || acctD.getAccountDescription().equalsIgnoreCase(ArConstants.CONTRACT_CONTROL_ACCOUNT)) {
acctDetails.add(acctD);
}
}
}
return acctDetails;
}
/**
* Sets the AccountReviewDetails attribute value.
*
* @param AccountReviewDetails The AccountReviewDetails to set.
*/
public void setAccountReviewDetails(List<ContractsGrantsLetterOfCreditReviewDetail> accountReviewDetails) {
this.accountReviewDetails = accountReviewDetails;
}
/**
* @see org.kuali.rice.krad.bo.BusinessObjectBase#toStringMapper()
*/
@SuppressWarnings("unchecked")
protected LinkedHashMap toStringMapper_RICE20_REFACTORME() {
LinkedHashMap m = new LinkedHashMap();
m.put(KFSPropertyConstants.DOCUMENT_NUMBER, this.documentNumber);
m.put(ArPropertyConstants.LETTER_OF_CREDIT_FUND_CODE, letterOfCreditFundCode);
m.put(ArPropertyConstants.LETTER_OF_CREDIT_FUND, letterOfCreditFund);
m.put(ArPropertyConstants.LETTER_OF_CREDIT_FUND_GROUP_CODE, letterOfCreditFundGroupCode);
m.put(ArPropertyConstants.LETTER_OF_CREDIT_FUND_GROUP, letterOfCreditFundGroup);
m.put(ArPropertyConstants.HEADER_REVIEW_DETAILS, headerReviewDetails);
m.put(ArPropertyConstants.ACCOUNT_REVIEW_DETAILS, accountReviewDetails);
return m;
}
/**
* Clear out the initially populated fields.
*/
public void clearInitFields() {
LOG.debug("clearDocument() started");
// Clearing document Init fields
setLetterOfCreditFundGroupCode(null);
setLetterOfCreditFundCode(null);
}
/**
* populate CGB LOC Review details based on the invoice info
*
* @param contractsGrantsInvoiceDocumentErrorLogs Collection used to hold any validation errors from the awards processed
* @return true if process succeeded, and there is at least one valid award for the LOC doc, false otherwise
*/
public boolean populateContractsGrantsLOCReviewDetails(Collection<ContractsGrantsInvoiceDocumentErrorLog> contractsGrantsInvoiceDocumentErrorLogs) {
boolean valid = true;
DateTimeService dateTimeService = SpringContext.getBean(DateTimeService.class);
ContractsGrantsInvoiceDocumentService contractsGrantsInvoiceDocumentService = SpringContext.getBean(ContractsGrantsInvoiceDocumentService.class);
ContractsGrantsInvoiceCreateDocumentService contractsGrantsInvoiceCreateDocumentService = SpringContext.getBean(ContractsGrantsInvoiceCreateDocumentService.class);
ContractsGrantsLetterOfCreditReviewDetail locReviewDtl;
Map<String, Object> criteria = new HashMap<String, Object>();
if (ObjectUtils.isNotNull(this.getLetterOfCreditFundGroupCode())) {
criteria.put("letterOfCreditFund.letterOfCreditFundGroupCode", this.getLetterOfCreditFundGroupCode());
}
if (ObjectUtils.isNotNull(this.getLetterOfCreditFundCode())) {
criteria.put("letterOfCreditFundCode", this.getLetterOfCreditFundCode());
}
// To exclude awards with milestones and predetermined schedule.
criteria.put(ArPropertyConstants.BILLING_FREQUENCY_CODE, ArConstants.LOC_BILLING_SCHEDULE_CODE);
List<ContractsAndGrantsBillingAward> awards = getContractsGrantsLetterOfCreditReviewDocumentService().getActiveAwardsByCriteria(criteria);
if (CollectionUtils.isEmpty(awards)) {
GlobalVariables.getMessageMap().putErrorForSectionId("Contracts & Grants LOC Review Initiation", ArKeyConstants.ContractsGrantsInvoiceConstants.ERROR_NO_AWARDS_RETRIEVED);
}
else {
List<ContractsAndGrantsBillingAward> validAwards = new ArrayList<ContractsAndGrantsBillingAward>();
validAwards = (List<ContractsAndGrantsBillingAward>) contractsGrantsInvoiceCreateDocumentService.validateAwards(awards, contractsGrantsInvoiceDocumentErrorLogs, null, ArConstants.ContractsAndGrantsInvoiceDocumentCreationProcessType.LOC.getCode());
if (CollectionUtils.isEmpty(validAwards)) {
GlobalVariables.getMessageMap().putWarningForSectionId("Contracts & Grants LOC Review Initiation", ArKeyConstants.ContractsGrantsInvoiceConstants.ERROR_AWARDS_INVALID);
valid = false;
}
else {
for (ContractsAndGrantsBillingAward award : validAwards) {
// To set the amount to draw for the award accounts as a whole.
final Map<String, KualiDecimal> awardAccountAmountsToDraw = getContractsGrantsLetterOfCreditReviewDocumentService().calculateAwardAccountAmountsToDraw(award, award.getActiveAwardAccounts());
KualiDecimal totalAmountToDraw = KualiDecimal.ZERO;
KualiDecimal amountAvailableToDraw = KualiDecimal.ZERO;
KualiDecimal totalClaimOnCashBalance = KualiDecimal.ZERO;
KualiDecimal totalAwardBudgetAmount = KualiDecimal.ZERO;
// Creating the header row here.
locReviewDtl = new ContractsGrantsLetterOfCreditReviewDetail();
locReviewDtl.setDocumentNumber(this.documentNumber);
locReviewDtl.setProposalNumber(award.getProposalNumber());
locReviewDtl.setAwardDocumentNumber(award.getAwardDocumentNumber());
locReviewDtl.setAgencyNumber(award.getAgencyNumber());
locReviewDtl.setCustomerNumber(award.getAgency().getCustomerNumber());
locReviewDtl.setAwardBeginningDate(award.getAwardBeginningDate());
locReviewDtl.setAwardEndingDate(award.getAwardEndingDate());
amountAvailableToDraw = getContractsGrantsLetterOfCreditReviewDocumentService().getAmountAvailableToDraw(award.getAwardTotalAmount(), award.getActiveAwardAccounts());
locReviewDtl.setAmountAvailableToDraw(amountAvailableToDraw);
if (ObjectUtils.isNotNull(award.getLetterOfCreditFund())) {
locReviewDtl.setLetterOfCreditAmount(award.getLetterOfCreditFund().getLetterOfCreditFundAmount());
}
headerReviewDetails.add(locReviewDtl);
final SystemOptions systemOption = getOptionsService().getCurrentYearOptions();
// Creating sub rows for the individual accounts.
for (ContractsAndGrantsBillingAwardAccount awardAccount : award.getActiveAwardAccounts()) {
final String awardAccountKey = getContractsGrantsLetterOfCreditReviewDocumentService().getAwardAccountKey(awardAccount);
locReviewDtl = new ContractsGrantsLetterOfCreditReviewDetail();
locReviewDtl.setDocumentNumber(this.documentNumber);
locReviewDtl.setProposalNumber(award.getProposalNumber());
locReviewDtl.setChartOfAccountsCode(awardAccount.getChartOfAccountsCode());
locReviewDtl.setAccountNumber(awardAccount.getAccountNumber());
locReviewDtl.setAccountExpirationDate(awardAccount.getAccount().getAccountExpirationDate());
locReviewDtl.setClaimOnCashBalance(awardAccountAmountsToDraw.get(awardAccountKey).negated());
totalClaimOnCashBalance = totalClaimOnCashBalance.add(locReviewDtl.getClaimOnCashBalance());
locReviewDtl.setAwardBudgetAmount(contractsGrantsInvoiceDocumentService.getBudgetAndActualsForAwardAccount(awardAccount, systemOption.getBudgetCheckingBalanceTypeCd(), award.getAwardBeginningDate()));
totalAwardBudgetAmount = totalAwardBudgetAmount.add(locReviewDtl.getAwardBudgetAmount());
if (ObjectUtils.isNotNull(awardAccount.getAccount().getContractControlAccountNumber()) && awardAccount.getAccountNumber().equalsIgnoreCase(awardAccount.getAccount().getContractControlAccountNumber())) {
locReviewDtl.setAccountDescription(ArConstants.CONTRACT_CONTROL_ACCOUNT);
}
else {
locReviewDtl.setAccountDescription(ArConstants.ACCOUNT);
}
locReviewDtl.setAmountToDraw(awardAccountAmountsToDraw.get(awardAccountKey));
locReviewDtl.setHiddenAmountToDraw(awardAccountAmountsToDraw.get(awardAccountKey));
totalAmountToDraw = totalAmountToDraw.add(locReviewDtl.getAmountToDraw());
accountReviewDetails.add(locReviewDtl);
}
// Amount to Draw for Header = Sum(Amount of Draw for individual accounts)
for (ContractsGrantsLetterOfCreditReviewDetail detail : getHeaderReviewDetails()) {// To identify the header row
if (ObjectUtils.isNotNull(detail.getAgencyNumber()) && ObjectUtils.isNull(detail.getAccountDescription()) && detail.getProposalNumber().equals(award.getProposalNumber())) {
detail.setAmountToDraw(totalAmountToDraw);
detail.setHiddenAmountToDraw(totalAmountToDraw);
detail.setClaimOnCashBalance(totalClaimOnCashBalance);
detail.setAwardBudgetAmount(totalAwardBudgetAmount);
}
}
}
}
}
return valid;
}
/**
* @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#prepareForSave() To check if the amount to Draw
* field has been changed and to set the award locReviewIndicator to true.
*/
@Override
public void prepareForSave() {
super.prepareForSave();
// 1. compare the hiddenamountodraw and amount to draw field.
for (ContractsGrantsLetterOfCreditReviewDetail detail : getAccountReviewDetails()) {
Map<String, Object> map = new HashMap<String, Object>();
map.put(KFSPropertyConstants.PROPOSAL_NUMBER, detail.getProposalNumber());
ContractsAndGrantsBillingAward award = SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(ContractsAndGrantsBillingAward.class).getExternalizableBusinessObject(ContractsAndGrantsBillingAward.class, map);
// to set funds Not Drawn as a difference between amountToDraw and hiddenAmountToDraw.
// To set amount to Draw to 0 if there are blank values, to avoid exceptions.
if (ObjectUtils.isNull(detail.getAmountToDraw()) ) {
detail.setAmountToDraw(KualiDecimal.ZERO);
}
detail.setFundsNotDrawn(detail.getHiddenAmountToDraw().subtract(detail.getAmountToDraw()));
if (detail.getFundsNotDrawn().isNegative()) {
GlobalVariables.getMessageMap().putError(ArPropertyConstants.FUNDS_NOT_DRAWN, ArKeyConstants.ContractsGrantsInvoiceConstants.ERROR_DOCUMENT_AMOUNT_TO_DRAW_INVALID);
detail.setFundsNotDrawn(KualiDecimal.ZERO);
detail.setAmountToDraw(detail.getHiddenAmountToDraw().subtract(detail.getFundsNotDrawn()));
}
}
// To sum up amount to draw values.
Set<Long> proposalNumberSet = new HashSet<Long>();
for (ContractsGrantsLetterOfCreditReviewDetail detail : getHeaderReviewDetails()) {
// Adding the awards to a set, to get unique values.
proposalNumberSet.add(detail.getProposalNumber());
}
// 2. create invoices. - independent whether the amounts were changed or not.
// To get the list of awards from the proposal Number set.
for (Long proposalNumber : proposalNumberSet) {
KualiDecimal totalAmountToDraw = KualiDecimal.ZERO;
for (ContractsGrantsLetterOfCreditReviewDetail detail : getAccountReviewDetails()) {// To identify the header row
if (ObjectUtils.isNotNull(detail.getAccountDescription()) && detail.getProposalNumber().equals(proposalNumber)) {
totalAmountToDraw = totalAmountToDraw.add(detail.getAmountToDraw());
}
}
for (ContractsGrantsLetterOfCreditReviewDetail detail : getHeaderReviewDetails()) {// To identify the header row
if (ObjectUtils.isNotNull(detail.getAgencyNumber()) && ObjectUtils.isNull(detail.getAccountDescription()) && detail.getProposalNumber().equals(proposalNumber)) {
detail.setAmountToDraw(totalAmountToDraw);
}
}
}
}
/**
* @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#doRouteStatusChange(org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO)
*/
@Override
public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
super.doRouteStatusChange(statusChangeEvent);
// performed only when document is in processed state
if (getDocumentHeader().getWorkflowDocument().isProcessed()) {
getContractsGrantsLetterOfCreditReviewDocumentService().generateContractsGrantsInvoiceDocuments(this);
}
}
public static ContractsGrantsLetterOfCreditReviewDocumentService getContractsGrantsLetterOfCreditReviewDocumentService() {
if (contractsGrantsLetterOfCreditReviewDocumentService == null) {
contractsGrantsLetterOfCreditReviewDocumentService = SpringContext.getBean(ContractsGrantsLetterOfCreditReviewDocumentService.class);
}
return contractsGrantsLetterOfCreditReviewDocumentService;
}
public static OptionsService getOptionsService() {
if (optionsService == null) {
optionsService = SpringContext.getBean(OptionsService.class);
}
return optionsService;
}
}