/*
* 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.batch.service.impl;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.AccountingPeriod;
import org.kuali.kfs.coa.service.AccountingPeriodService;
import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAward;
import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAwardAccount;
import org.kuali.kfs.module.ar.ArConstants;
import org.kuali.kfs.module.ar.batch.service.VerifyBillingFrequencyService;
import org.kuali.kfs.module.ar.businessobject.Bill;
import org.kuali.kfs.module.ar.businessobject.BillingPeriod;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.krad.service.BusinessObjectService;
public class VerifyBillingFrequencyServiceImpl implements VerifyBillingFrequencyService {
protected BusinessObjectService businessObjectService;
protected AccountingPeriodService accountingPeriodService;
protected UniversityDateService universityDateService;
protected DateTimeService dateTimeService;
protected static final Set<String> invalidPeriodCodes = new TreeSet<>();
static {
invalidPeriodCodes.add(KFSConstants.MONTH13);
invalidPeriodCodes.add(KFSConstants.PERIOD_CODE_ANNUAL_BALANCE);
invalidPeriodCodes.add(KFSConstants.PERIOD_CODE_BEGINNING_BALANCE);
invalidPeriodCodes.add(KFSConstants.PERIOD_CODE_CG_BEGINNING_BALANCE);
}
@Override
public boolean validateBillingFrequency(ContractsAndGrantsBillingAward award) {
return validateBillingFrequency(award, award.getLastBilledDate());
}
@Override
public boolean validateBillingFrequency(ContractsAndGrantsBillingAward award, ContractsAndGrantsBillingAwardAccount awardAccount) {
return validateBillingFrequency(award, awardAccount.getCurrentLastBilledDate());
}
protected boolean validateBillingFrequency(ContractsAndGrantsBillingAward award, Date lastBilledDate) {
final Date today = getDateTimeService().getCurrentSqlDate();
AccountingPeriod currPeriod = accountingPeriodService.getByDate(today);
BillingPeriod billingPeriod = getStartDateAndEndDateOfPreviousBillingPeriod(award, currPeriod);
if (!billingPeriod.isBillable()) {
return false;
}
if (billingPeriod.getStartDate().after(billingPeriod.getEndDate())) {
return false;
}
return calculateIfWithinGracePeriod(today, billingPeriod.getEndDate(), billingPeriod.getStartDate(), lastBilledDate, award.getBillingFrequency().getGracePeriodDays());
}
/**
* This method checks if a given moment of time is within an accounting period, or its billing frequency grace period.
*
* @param today a date to check if it is within the period
* @param previousAccountingPeriodEndDate the end of the accounting period
* @param previousAccountingPeriodStartDate the start of the accounting period
* @return true if a given moment in time is within an accounting period or an billing frequency grace period
*/
@Override
public boolean calculateIfWithinGracePeriod(Date today, Date previousAccountingPeriodEndDate, Date previousAccountingPeriodStartDate, Date lastBilledDate, int gracePeriodDays) {
if (previousAccountingPeriodEndDate == null || previousAccountingPeriodStartDate == null) {
throw new IllegalArgumentException("invalid (null) previousAccountingPeriodEndDate or previousAccountingPeriodStartDate");
}
final int todayAsComparableDate = comparableDateForm(today);
final int previousPeriodClose = comparableDateForm(previousAccountingPeriodEndDate);
final int previousPeriodBegin = comparableDateForm(previousAccountingPeriodStartDate);
int lastBilled = -1;
if (lastBilledDate != null) {
lastBilled = comparableDateForm(lastBilledDate);
}
final int gracePeriodClose = previousPeriodClose + gracePeriodDays;
final int gracePeriodAfterLastBilled = lastBilled + gracePeriodDays;
return (todayAsComparableDate >= previousPeriodBegin && gracePeriodClose <= todayAsComparableDate && (lastBilledDate == null || todayAsComparableDate > gracePeriodAfterLastBilled));
}
@Override
public BillingPeriod getStartDateAndEndDateOfPreviousBillingPeriod(ContractsAndGrantsBillingAward award, AccountingPeriod currPeriod) {
return BillingPeriod.determineBillingPeriodPriorTo(award.getAwardBeginningDate(), this.dateTimeService.getCurrentSqlDate(), award.getLastBilledDate(), award.getBillingFrequencyCode(), this.accountingPeriodService);
}
@Override
public ArrayList<Date> getSortedListOfPeriodEndDatesOfCurrentFiscalYear(AccountingPeriod currPeriod) {
ArrayList<Date> acctPeriodEndDateList = new ArrayList<>();
Map<String, Object> fieldValues = new HashMap<>();
fieldValues.put(KFSConstants.ACCOUNTING_PERIOD_ACTIVE_INDICATOR_FIELD, Boolean.TRUE);
fieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, currPeriod.getUniversityFiscalYear());
Collection<AccountingPeriod> acctPeriodList = businessObjectService.findMatching(AccountingPeriod.class, fieldValues);
if (acctPeriodList != null) {
for (AccountingPeriod acctPeriod : acctPeriodList) {
if (!isInvalidPeriodCode(acctPeriod)) {
acctPeriodEndDateList.add(acctPeriod.getUniversityFiscalPeriodEndDate());
}
}
if (acctPeriodEndDateList == null || acctPeriodEndDateList.size() != 12) {
String fiscalYear = "; fiscalYear: " + currPeriod.getUniversityFiscalYear();
String size = "; size = " + ((acctPeriodEndDateList == null) ? "null" : acctPeriodEndDateList.size());
throw new IllegalArgumentException("invalid (null) Accounting-Period-End-Date List" + fiscalYear + size);
}
java.util.Collections.sort(acctPeriodEndDateList);
return acctPeriodEndDateList;
} else {
throw new IllegalArgumentException("invalid (null) Accounting-Period-End-Date List");
}
}
/**
* This method returns a date as an approximate count of days since the BCE epoch.
*
* @param d the date to convert
* @return an integer count of days, very approximate
*/
public int comparableDateForm(Date d) {
java.util.Calendar cal = new java.util.GregorianCalendar();
cal.setTime(d);
return cal.get(java.util.Calendar.YEAR) * 365 + cal.get(java.util.Calendar.DAY_OF_YEAR);
}
/**
* Given a day, this method calculates the next day of that day was.
*
* @param d date to find the next day for
* @return date of the next day of the given day
*/
public Date calculateNextDay(Date d) {
java.util.Calendar cal = new java.util.GregorianCalendar();
cal.setTime(d);
cal.add(java.util.Calendar.DAY_OF_YEAR, 1);
return new Date(cal.getTimeInMillis());
}
/**
* This checks to see if the period code is empty or invalid ("13", "AB", "BB", "CB")
*
* @param period
* @return
*/
protected boolean isInvalidPeriodCode(AccountingPeriod period) {
String periodCode = period.getUniversityFiscalPeriodCode();
if (StringUtils.isBlank(periodCode)) {
throw new IllegalArgumentException("invalid (null) universityFiscalPeriodCode (" + periodCode + ")for" + period);
}
return invalidPeriodCodes.contains(periodCode);
}
/**
* Sets the accountingPeriodService attribute value.
*
* @param accountingPeriodService The accountingPeriodService to set.
*/
public void setAccountingPeriodService(AccountingPeriodService accountingPeriodService) {
this.accountingPeriodService = accountingPeriodService;
}
/**
* Sets the universityDateService attribute value.
*
* @param universityDateService The universityDateService to set.
*/
public void setUniversityDateService(UniversityDateService universityDateService) {
this.universityDateService = universityDateService;
}
public BusinessObjectService getBusinessObjectService() {
return businessObjectService;
}
public void setBusinessObjectService(BusinessObjectService businessObjectService) {
this.businessObjectService = businessObjectService;
}
public DateTimeService getDateTimeService() {
return dateTimeService;
}
public void setDateTimeService(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
}