001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.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/ecl2.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.kfs.module.ar.document.validation.impl;
017
018 import static org.kuali.kfs.sys.document.validation.impl.AccountingDocumentRuleBaseConstants.ERROR_PATH.DOCUMENT_ERROR_PREFIX;
019
020 import java.sql.Timestamp;
021
022 import org.kuali.kfs.module.ar.ArConstants;
023 import org.kuali.kfs.module.ar.ArKeyConstants;
024 import org.kuali.kfs.module.ar.ArPropertyConstants;
025 import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument;
026 import org.kuali.kfs.sys.document.validation.GenericValidation;
027 import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
028 import org.kuali.rice.kns.service.DateTimeService;
029 import org.kuali.rice.kns.service.ParameterService;
030 import org.kuali.rice.kns.util.DateUtils;
031 import org.kuali.rice.kns.util.GlobalVariables;
032
033 public class CustomerInvoiceDueDateValidation extends GenericValidation {
034
035 private CustomerInvoiceDocument customerInvoiceDocument;
036 private DateTimeService dateTimeService;
037 private ParameterService parameterService;
038
039 public boolean validate(AttributedDocumentEvent event) {
040
041 Timestamp dueDateTimestamp = new Timestamp(customerInvoiceDocument.getInvoiceDueDate().getTime());
042 Timestamp billingDateTimestamp = new Timestamp(dateTimeService.getCurrentDate().getTime());
043
044 if (dueDateTimestamp.before(billingDateTimestamp) || dueDateTimestamp.equals(billingDateTimestamp)) {
045 GlobalVariables.getMessageMap().putError(DOCUMENT_ERROR_PREFIX + ArPropertyConstants.CustomerInvoiceDocumentFields.INVOICE_DUE_DATE, ArKeyConstants.ERROR_CUSTOMER_INVOICE_DOCUMENT_INVALID_INVOICE_DUE_DATE_BEFORE_OR_EQUAL_TO_BILLING_DATE);
046 return false;
047 }
048 else {
049 long diffInDays = getDifferenceInDays(billingDateTimestamp, dueDateTimestamp); //DateUtils.getDifferenceInDays(billingDateTimestamp, dueDateTimestamp);
050 int maxNumOfDaysAfterCurrentDateForInvoiceDueDate = Integer.parseInt(parameterService.getParameterValue(CustomerInvoiceDocument.class, ArConstants.MAXIMUM_NUMBER_OF_DAYS_AFTER_CURRENT_DATE_FOR_INVOICE_DUE_DATE));
051 if (diffInDays >= maxNumOfDaysAfterCurrentDateForInvoiceDueDate) {
052 GlobalVariables.getMessageMap().putError(DOCUMENT_ERROR_PREFIX + ArPropertyConstants.CustomerInvoiceDocumentFields.INVOICE_DUE_DATE, ArKeyConstants.ERROR_CUSTOMER_INVOICE_DOCUMENT_INVALID_INVOICE_DUE_DATE_MORE_THAN_X_DAYS, maxNumOfDaysAfterCurrentDateForInvoiceDueDate + "");
053 return false;
054 }
055
056 }
057
058 return true;
059 }
060
061 /**
062 *
063 * This method calculates the difference in days between the two timestamps provided.
064 *
065 * This method is used instead of DateUtils.getDifferenceInDays() because there is a rounding issue within the timestamp that exists which must be dealt with to
066 * prevent improper calculations. This issue is similar to the problems that exist with adding and subtracting doubles and the inherently bad way that Java handles
067 * numbers.
068 *
069 * The approach used within DateUtils does not offer enough accuracy to calculate the difference consistently and accurately.
070 *
071 * @param t1
072 * @param t2
073 * @return The difference in days between the two given timestamps.
074 */
075 public static long getDifferenceInDays (Timestamp t1, Timestamp t2) {
076 // Make sure the result is always > 0
077 if (t1.compareTo (t2) < 0) {
078 Timestamp tmp = t1;
079 t1 = t2;
080 t2 = tmp;
081 }
082
083 // Timestamps mix milli and nanoseconds in the API, so we have to separate the two
084 long diffSeconds = (t1.getTime () / 1000) - (t2.getTime () / 1000);
085 // For normals dates, we have millisecond precision
086 int nano1 = ((int) t1.getTime () % 1000) * 1000000;
087 nano1 = t1.getNanos ();
088 int nano2 = ((int) t2.getTime () % 1000) * 1000000;
089 nano2 = t2.getNanos ();
090
091 int diffNanos = nano1 - nano2;
092 if (diffNanos < 0) {
093 // Borrow one second
094 diffSeconds --;
095 diffNanos += 1000000000;
096 }
097
098 // mix nanos and millis again
099 Timestamp result = new Timestamp ((diffSeconds * 1000) + (diffNanos / 1000000));
100 // setNanos() with a value of in the millisecond range doesn't affect the value of the time field
101 // while milliseconds in the time field will modify nanos! Damn, this API is a *mess*
102 result.setNanos (diffNanos);
103 return result.getDate();
104 }
105
106 public CustomerInvoiceDocument getCustomerInvoiceDocument() {
107 return customerInvoiceDocument;
108 }
109
110 public void setCustomerInvoiceDocument(CustomerInvoiceDocument customerInvoiceDocument) {
111 this.customerInvoiceDocument = customerInvoiceDocument;
112 }
113
114 public DateTimeService getDateTimeService() {
115 return dateTimeService;
116 }
117
118 public void setDateTimeService(DateTimeService dateTimeService) {
119 this.dateTimeService = dateTimeService;
120 }
121
122 public ParameterService getParameterService() {
123 return parameterService;
124 }
125
126 public void setParameterService(ParameterService parameterService) {
127 this.parameterService = parameterService;
128 }
129 }