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.pdp.service.impl;
017
018 import java.sql.Timestamp;
019 import java.text.MessageFormat;
020 import java.text.ParseException;
021 import java.util.ArrayList;
022 import java.util.Calendar;
023 import java.util.List;
024
025 import org.apache.commons.lang.StringUtils;
026 import org.kuali.kfs.coa.businessobject.Account;
027 import org.kuali.kfs.coa.businessobject.ObjectCode;
028 import org.kuali.kfs.coa.businessobject.ProjectCode;
029 import org.kuali.kfs.coa.businessobject.SubAccount;
030 import org.kuali.kfs.coa.businessobject.SubObjectCode;
031 import org.kuali.kfs.coa.service.AccountService;
032 import org.kuali.kfs.coa.service.ObjectCodeService;
033 import org.kuali.kfs.coa.service.ProjectCodeService;
034 import org.kuali.kfs.coa.service.SubAccountService;
035 import org.kuali.kfs.coa.service.SubObjectCodeService;
036 import org.kuali.kfs.pdp.PdpConstants;
037 import org.kuali.kfs.pdp.PdpKeyConstants;
038 import org.kuali.kfs.pdp.PdpParameterConstants;
039 import org.kuali.kfs.pdp.PdpPropertyConstants;
040 import org.kuali.kfs.pdp.businessobject.AccountingChangeCode;
041 import org.kuali.kfs.pdp.businessobject.CustomerProfile;
042 import org.kuali.kfs.pdp.businessobject.PayeeType;
043 import org.kuali.kfs.pdp.businessobject.PaymentAccountDetail;
044 import org.kuali.kfs.pdp.businessobject.PaymentAccountHistory;
045 import org.kuali.kfs.pdp.businessobject.PaymentDetail;
046 import org.kuali.kfs.pdp.businessobject.PaymentFileLoad;
047 import org.kuali.kfs.pdp.businessobject.PaymentGroup;
048 import org.kuali.kfs.pdp.businessobject.PaymentStatus;
049 import org.kuali.kfs.pdp.dataaccess.PaymentFileLoadDao;
050 import org.kuali.kfs.pdp.service.CustomerProfileService;
051 import org.kuali.kfs.pdp.service.PaymentFileValidationService;
052 import org.kuali.kfs.sys.KFSConstants;
053 import org.kuali.kfs.sys.businessobject.Bank;
054 import org.kuali.kfs.sys.businessobject.OriginationCode;
055 import org.kuali.kfs.sys.context.SpringContext;
056 import org.kuali.kfs.sys.service.BankService;
057 import org.kuali.kfs.sys.service.OriginationCodeService;
058 import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
059 import org.kuali.rice.kew.exception.WorkflowException;
060 import org.kuali.rice.kns.bo.KualiCodeBase;
061 import org.kuali.rice.kns.service.BusinessObjectService;
062 import org.kuali.rice.kns.service.DateTimeService;
063 import org.kuali.rice.kns.service.KualiConfigurationService;
064 import org.kuali.rice.kns.service.ParameterService;
065 import org.kuali.rice.kns.util.KualiDecimal;
066 import org.kuali.rice.kns.util.MessageMap;
067 import org.kuali.rice.kns.workflow.service.KualiWorkflowInfo;
068 import org.springframework.transaction.annotation.Transactional;
069
070 /**
071 * @see org.kuali.kfs.pdp.batch.service.PaymentFileValidationService
072 */
073 @Transactional
074 public class PaymentFileValidationServiceImpl implements PaymentFileValidationService {
075 private CustomerProfileService customerProfileService;
076 private PaymentFileLoadDao paymentFileLoadDao;
077 private ParameterService parameterService;
078 private KualiConfigurationService kualiConfigurationService;
079 private DateTimeService dateTimeService;
080 private AccountService accountService;
081 private SubAccountService subAccountService;
082 private ObjectCodeService objectCodeService;
083 private SubObjectCodeService subObjectCodeService;
084 private ProjectCodeService projectCodeService;
085 private BankService bankService;
086 private OriginationCodeService originationCodeService;
087 private KualiWorkflowInfo workflowInfoService;
088 private BusinessObjectService businessObjectService;
089
090 /**
091 * @see org.kuali.kfs.pdp.batch.service.PaymentFileValidationService#doHardEdits(org.kuali.kfs.pdp.businessobject.PaymentFile,
092 * org.kuali.rice.kns.util.ErrorMap)
093 */
094 public void doHardEdits(PaymentFileLoad paymentFile, MessageMap errorMap) {
095 processHeaderValidation(paymentFile, errorMap);
096
097 if (errorMap.hasNoErrors()) {
098 processGroupValidation(paymentFile, errorMap);
099 }
100
101 if (errorMap.hasNoErrors()) {
102 processTrailerValidation(paymentFile, errorMap);
103 }
104 }
105
106 /**
107 * Validates payment file header fields <li>Checks customer exists in customer profile table and is active</li>
108 *
109 * @param paymentFile payment file object
110 * @param errorMap map in which errors will be added to
111 */
112 protected void processHeaderValidation(PaymentFileLoad paymentFile, MessageMap errorMap) {
113 CustomerProfile customer = customerProfileService.get(paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit());
114 if (customer == null) {
115 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_CUSTOMER, paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit());
116 }
117 else {
118 if (!customer.isActive()) {
119 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INACTIVE_CUSTOMER, paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit());
120 }
121 else {
122 paymentFile.setCustomer(customer);
123 }
124 }
125 }
126
127 /**
128 * Validates payment file trailer fields <li>Reconciles actual to expected payment count and totals</li> <li>Verifies the batch
129 * is not a duplicate</li>
130 *
131 * @param paymentFile payment file object
132 * @param errorMap map in which errors will be added to
133 */
134 protected void processTrailerValidation(PaymentFileLoad paymentFile, MessageMap errorMap) {
135 // compare trailer payment count to actual count loaded
136 if (paymentFile.getActualPaymentCount() != paymentFile.getPaymentCount()) {
137 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYMENT_COUNT_MISMATCH, Integer.toString(paymentFile.getPaymentCount()), Integer.toString(paymentFile.getActualPaymentCount()));
138 }
139
140 // compare trailer total amount with actual total amount
141 if (paymentFile.getCalculatedPaymentTotalAmount().compareTo(paymentFile.getPaymentTotalAmount()) != 0) {
142 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYMENT_TOTAL_MISMATCH, paymentFile.getPaymentTotalAmount().toString(), paymentFile.getCalculatedPaymentTotalAmount().toString());
143 }
144
145 // Check to see if this is a duplicate batch
146 Timestamp now = new Timestamp(paymentFile.getCreationDate().getTime());
147
148 if (paymentFileLoadDao.isDuplicateBatch(paymentFile.getCustomer(), paymentFile.getPaymentCount(), paymentFile.getPaymentTotalAmount().bigDecimalValue(), now)) {
149 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_DUPLICATE_BATCH);
150 }
151 }
152
153 /**
154 * Validates payment file groups <li>Checks number of note lines needed is not above the configured maximum allowed</li> <li>
155 * Verifies group total is not negative</li> <li>Verifies detail accounting total equals net payment amount</li>
156 *
157 * @param paymentFile payment file object
158 * @param errorMap map in which errors will be added to
159 */
160 protected void processGroupValidation(PaymentFileLoad paymentFile, MessageMap errorMap) {
161 int groupCount = 0;
162 for (PaymentGroup paymentGroup : paymentFile.getPaymentGroups()) {
163 groupCount++;
164
165 int noteLineCount = 0;
166 int detailCount = 0;
167
168 // verify payee id and owner code if customer requires them to be filled in
169 if (paymentFile.getCustomer().getPayeeIdRequired() && StringUtils.isBlank(paymentGroup.getPayeeId())) {
170 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYEE_ID_REQUIRED, Integer.toString(groupCount));
171 }
172
173 if (paymentFile.getCustomer().getOwnershipCodeRequired() && StringUtils.isBlank(paymentGroup.getPayeeOwnerCd())) {
174 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYEE_OWNER_CODE, Integer.toString(groupCount));
175 }
176
177 // validate payee id type
178 if (StringUtils.isNotBlank(paymentGroup.getPayeeIdTypeCd())) {
179 PayeeType payeeType = (PayeeType) businessObjectService.findBySinglePrimaryKey(PayeeType.class, paymentGroup.getPayeeIdTypeCd());
180 if (payeeType == null) {
181 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_PAYEE_ID_TYPE, Integer.toString(groupCount), paymentGroup.getPayeeIdTypeCd());
182 }
183 }
184
185 // validate bank
186 String bankCode = paymentGroup.getBankCode();
187 if (StringUtils.isNotBlank(bankCode)) {
188 Bank bank = bankService.getByPrimaryId(bankCode);
189 if (bank == null) {
190 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_BANK_CODE, Integer.toString(groupCount), bankCode);
191 }
192 else if (!bank.isActive()) {
193 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INACTIVE_BANK_CODE, Integer.toString(groupCount), bankCode);
194 }
195 }
196
197 KualiDecimal groupTotal = KualiDecimal.ZERO;
198 for (PaymentDetail paymentDetail : paymentGroup.getPaymentDetails()) {
199 detailCount++;
200
201 noteLineCount++; // Add a line to print the invoice number
202 noteLineCount = noteLineCount + paymentDetail.getNotes().size();
203
204 if ((paymentDetail.getNetPaymentAmount() == null) && (!paymentDetail.isDetailAmountProvided())) {
205 paymentDetail.setNetPaymentAmount(paymentDetail.getAccountTotal());
206 }
207 else if ((paymentDetail.getNetPaymentAmount() == null) && (paymentDetail.isDetailAmountProvided())) {
208 paymentDetail.setNetPaymentAmount(paymentDetail.getCalculatedPaymentAmount());
209 }
210
211 // compare net to accounting segments
212 if (paymentDetail.getAccountTotal().compareTo(paymentDetail.getNetPaymentAmount()) != 0) {
213 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_DETAIL_TOTAL_MISMATCH, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getAccountTotal().toString(), paymentDetail.getNetPaymentAmount().toString());
214 }
215
216 // validate origin code if given
217 if (StringUtils.isNotBlank(paymentDetail.getFinancialSystemOriginCode())) {
218 OriginationCode originationCode = originationCodeService.getByPrimaryKey(paymentDetail.getFinancialSystemOriginCode());
219 if (originationCode == null) {
220 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_ORIGIN_CODE, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getFinancialSystemOriginCode());
221 }
222 }
223
224 // validate doc type if given
225 if (StringUtils.isNotBlank(paymentDetail.getFinancialDocumentTypeCode())) {
226 try {
227 if (!workflowInfoService.isCurrentActiveDocumentType(paymentDetail.getFinancialDocumentTypeCode())) {
228 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_DOC_TYPE, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getFinancialDocumentTypeCode());
229 }
230 }
231 catch (WorkflowException e) {
232 throw new RuntimeException("error trying to validate document type: " + e.getMessage(), e);
233 }
234 }
235
236 groupTotal = groupTotal.add(paymentDetail.getNetPaymentAmount());
237 }
238
239 // verify total for group is not negative
240 if (groupTotal.doubleValue() < 0) {
241 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_NEGATIVE_GROUP_TOTAL, Integer.toString(groupCount));
242 }
243
244 // check that the number of detail items and note lines will fit on a check stub
245 if (noteLineCount > getMaxNoteLines()) {
246 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_MAX_NOTE_LINES, Integer.toString(groupCount), Integer.toString(noteLineCount), Integer.toString(getMaxNoteLines()));
247 }
248 }
249 }
250
251 /**
252 * @see org.kuali.kfs.pdp.service.PaymentFileValidationService#doSoftEdits(org.kuali.kfs.pdp.businessobject.PaymentFile)
253 */
254 public List<String> doSoftEdits(PaymentFileLoad paymentFile) {
255 List<String> warnings = new ArrayList<String>();
256
257 CustomerProfile customer = customerProfileService.get(paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit());
258
259 // check payment amount does not exceed the configured threshold amount of this customer
260 if (paymentFile.getPaymentTotalAmount().compareTo(customer.getFileThresholdAmount()) > 0) {
261 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_FILE_THRESHOLD, paymentFile.getPaymentTotalAmount().toString(), customer.getFileThresholdAmount().toString());
262 paymentFile.setFileThreshold(true);
263 }
264
265 processGroupSoftEdits(paymentFile, customer, warnings);
266
267 return warnings;
268 }
269
270 /**
271 * Set defaults for group fields and do tax checks.
272 *
273 * @param paymentFile payment file object
274 * @param customer payment customer
275 * @param warnings <code>List</code> list of accumulated warning messages
276 */
277 public void processGroupSoftEdits(PaymentFileLoad paymentFile, CustomerProfile customer, List<String> warnings) {
278 PaymentStatus openStatus = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.OPEN);
279
280 for (PaymentGroup paymentGroup : paymentFile.getPaymentGroups()) {
281 paymentGroup.setBatchId(paymentFile.getBatchId());
282 paymentGroup.setPaymentStatusCode(openStatus.getCode());
283 paymentGroup.setPaymentStatus(openStatus);
284 paymentGroup.setPayeeName(paymentGroup.getPayeeName().toUpperCase());
285
286 // Set defaults for missing information
287 defaultGroupIndicators(paymentGroup);
288
289 // Tax Group Requirements for automatic Holding
290 checkForTaxEmailRequired(paymentFile, paymentGroup, customer);
291
292 // do edits on detail lines
293 for (PaymentDetail paymentDetail : paymentGroup.getPaymentDetails()) {
294 paymentDetail.setPaymentGroupId(paymentGroup.getId());
295
296 processDetailSoftEdits(paymentFile, customer, paymentDetail, warnings);
297 }
298
299 }
300 }
301
302 /**
303 * Set default fields on detail line and check amount against customer threshold.
304 *
305 * @param paymentFile payment file object
306 * @param customer payment customer
307 * @param paymentDetail <code>PaymentDetail</code> object to process
308 * @param warnings <code>List</code> list of accumulated warning messages
309 */
310 protected void processDetailSoftEdits(PaymentFileLoad paymentFile, CustomerProfile customer, PaymentDetail paymentDetail, List<String> warnings) {
311 updateDetailAmounts(paymentDetail);
312
313 // Check net payment amount
314 KualiDecimal testAmount = paymentDetail.getNetPaymentAmount();
315 if (testAmount.compareTo(customer.getPaymentThresholdAmount()) > 0) {
316 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_DETAIL_THRESHOLD, testAmount.toString(), customer.getPaymentThresholdAmount().toString());
317 paymentFile.setDetailThreshold(true);
318 paymentFile.getThresholdPaymentDetails().add(paymentDetail);
319 }
320
321 // set invoice date if it doesn't exist
322 if (paymentDetail.getInvoiceDate() == null) {
323 paymentDetail.setInvoiceDate(dateTimeService.getCurrentSqlDate());
324 }
325
326 if (paymentDetail.getPrimaryCancelledPayment() == null) {
327 paymentDetail.setPrimaryCancelledPayment(Boolean.FALSE);
328 }
329
330 // do accounting edits
331 for (PaymentAccountDetail paymentAccountDetail : paymentDetail.getAccountDetail()) {
332 paymentAccountDetail.setPaymentDetailId(paymentDetail.getId());
333
334 processAccountSoftEdits(paymentFile, customer, paymentAccountDetail, warnings);
335 }
336 }
337
338 /**
339 * Set default fields on account line and perform account field existence checks
340 *
341 * @param paymentFile payment file object
342 * @param customer payment customer
343 * @param paymentAccountDetail <code>PaymentAccountDetail</code> object to process
344 * @param warnings <code>List</code> list of accumulated warning messages
345 */
346 protected void processAccountSoftEdits(PaymentFileLoad paymentFile, CustomerProfile customer, PaymentAccountDetail paymentAccountDetail, List<String> warnings) {
347 List<PaymentAccountHistory> changeRecords = paymentAccountDetail.getAccountHistory();
348
349 // uppercase chart
350 paymentAccountDetail.setFinChartCode(paymentAccountDetail.getFinChartCode().toUpperCase());
351
352 // only do accounting edits if required by customer
353 if (customer.getAccountingEditRequired()) {
354 // check account number
355 Account account = accountService.getByPrimaryId(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr());
356 if (account == null) {
357 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_ACCOUNT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr());
358
359 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_ACCOUNT);
360 replaceAccountingString(objChangeCd, changeRecords, customer, paymentAccountDetail);
361 }
362 else {
363 // check sub account code
364 if (StringUtils.isNotBlank(paymentAccountDetail.getSubAccountNbr())) {
365 SubAccount subAccount = subAccountService.getByPrimaryId(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getSubAccountNbr());
366 if (subAccount == null) {
367 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_SUB_ACCOUNT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getSubAccountNbr());
368
369 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_SUB_ACCOUNT);
370 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_ACCOUNT_DB_COLUMN_NAME, KFSConstants.getDashSubAccountNumber(), paymentAccountDetail.getSubAccountNbr(), objChangeCd));
371
372 paymentAccountDetail.setSubAccountNbr(KFSConstants.getDashSubAccountNumber());
373 }
374 }
375
376 // check object code
377 ObjectCode objectCode = objectCodeService.getByPrimaryIdForCurrentYear(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getFinObjectCode());
378 if (objectCode == null) {
379 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_OBJECT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getFinObjectCode());
380
381 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_OBJECT);
382 replaceAccountingString(objChangeCd, changeRecords, customer, paymentAccountDetail);
383 }
384
385 // check sub object code
386 else if (StringUtils.isNotBlank(paymentAccountDetail.getFinSubObjectCode())) {
387 SubObjectCode subObjectCode = subObjectCodeService.getByPrimaryIdForCurrentYear(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getFinObjectCode(), paymentAccountDetail.getFinSubObjectCode());
388 if (subObjectCode == null) {
389 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_SUB_OBJECT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getFinObjectCode(), paymentAccountDetail.getFinSubObjectCode());
390
391 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_SUB_OBJECT);
392 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_OBJECT_DB_COLUMN_NAME, KFSConstants.getDashFinancialSubObjectCode(), paymentAccountDetail.getFinSubObjectCode(), objChangeCd));
393
394 paymentAccountDetail.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
395 }
396 }
397 }
398
399 // check project code
400 if (StringUtils.isNotBlank(paymentAccountDetail.getProjectCode())) {
401 ProjectCode projectCode = projectCodeService.getByPrimaryId(paymentAccountDetail.getProjectCode());
402 if (projectCode == null) {
403 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_PROJECT, paymentAccountDetail.getProjectCode());
404
405 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_PROJECT);
406 changeRecords.add(newAccountHistory(PdpPropertyConstants.PROJECT_DB_COLUMN_NAME, KFSConstants.getDashProjectCode(), paymentAccountDetail.getProjectCode(), objChangeCd));
407 paymentAccountDetail.setProjectCode(KFSConstants.getDashProjectCode());
408 }
409 }
410 }
411
412 // change nulls into ---'s for the fields that need it
413 if (StringUtils.isBlank(paymentAccountDetail.getFinSubObjectCode())) {
414 paymentAccountDetail.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
415 }
416
417 if (StringUtils.isBlank(paymentAccountDetail.getSubAccountNbr())) {
418 paymentAccountDetail.setSubAccountNbr(KFSConstants.getDashSubAccountNumber());
419 }
420
421 if (StringUtils.isBlank(paymentAccountDetail.getProjectCode())) {
422 paymentAccountDetail.setProjectCode(KFSConstants.getDashProjectCode());
423 }
424
425 }
426
427 /**
428 * Replaces the entire accounting string with defaults from the customer profile.
429 *
430 * @param objChangeCd code indicating reason for change
431 * @param changeRecords <code>List</code> of <code>PaymentAccountHistory</code> records
432 * @param customer profile of payment customer
433 * @param paymentAccountDetail account detail record
434 */
435 protected void replaceAccountingString(KualiCodeBase objChangeCd, List<PaymentAccountHistory> changeRecords, CustomerProfile customer, PaymentAccountDetail paymentAccountDetail) {
436 changeRecords.add(newAccountHistory(PdpPropertyConstants.CHART_DB_COLUMN_NAME, customer.getDefaultChartCode(), paymentAccountDetail.getFinChartCode(), objChangeCd));
437 changeRecords.add(newAccountHistory(PdpPropertyConstants.ACCOUNT_DB_COLUMN_NAME, customer.getDefaultAccountNumber(), paymentAccountDetail.getAccountNbr(), objChangeCd));
438 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_ACCOUNT_DB_COLUMN_NAME, customer.getDefaultSubAccountNumber(), paymentAccountDetail.getSubAccountNbr(), objChangeCd));
439 changeRecords.add(newAccountHistory(PdpPropertyConstants.OBJECT_DB_COLUMN_NAME, customer.getDefaultObjectCode(), paymentAccountDetail.getFinObjectCode(), objChangeCd));
440 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_OBJECT_DB_COLUMN_NAME, customer.getDefaultSubObjectCode(), paymentAccountDetail.getFinSubObjectCode(), objChangeCd));
441
442 paymentAccountDetail.setFinChartCode(customer.getDefaultChartCode());
443 paymentAccountDetail.setAccountNbr(customer.getDefaultAccountNumber());
444 if (StringUtils.isNotBlank(customer.getDefaultSubAccountNumber())) {
445 paymentAccountDetail.setSubAccountNbr(customer.getDefaultSubAccountNumber());
446 }
447 else {
448 paymentAccountDetail.setSubAccountNbr(KFSConstants.getDashSubAccountNumber());
449 }
450 paymentAccountDetail.setFinObjectCode(customer.getDefaultObjectCode());
451 if (StringUtils.isNotBlank(customer.getDefaultSubAccountNumber())) {
452 paymentAccountDetail.setFinSubObjectCode(customer.getDefaultSubObjectCode());
453 }
454 else {
455 paymentAccountDetail.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
456 }
457 }
458
459 /**
460 * Helper method to construct a new payment account history record
461 *
462 * @param attName name of field that has changed
463 * @param newValue new value for the field
464 * @param oldValue field value that was changed
465 * @param changeCode code indicating reason for change
466 * @return <code>PaymentAccountHistory</code>
467 */
468 protected PaymentAccountHistory newAccountHistory(String attName, String newValue, String oldValue, KualiCodeBase changeCode) {
469 PaymentAccountHistory paymentAccountHistory = new PaymentAccountHistory();
470
471 paymentAccountHistory.setAcctAttributeName(attName);
472 paymentAccountHistory.setAcctAttributeNewValue(newValue);
473 paymentAccountHistory.setAcctAttributeOrigValue(oldValue);
474 paymentAccountHistory.setAcctChangeDate(dateTimeService.getCurrentTimestamp());
475 paymentAccountHistory.setAccountingChange((AccountingChangeCode) changeCode);
476
477 return paymentAccountHistory;
478 }
479
480 /**
481 * Sets null amount fields to 0
482 *
483 * @param paymentDetail <code>PaymentDetail</code> to update
484 */
485 protected void updateDetailAmounts(PaymentDetail paymentDetail) {
486 KualiDecimal zero = KualiDecimal.ZERO;
487
488 if (paymentDetail.getInvTotDiscountAmount() == null) {
489 paymentDetail.setInvTotDiscountAmount(zero);
490 }
491
492 if (paymentDetail.getInvTotShipAmount() == null) {
493 paymentDetail.setInvTotShipAmount(zero);
494 }
495
496 if (paymentDetail.getInvTotOtherDebitAmount() == null) {
497 paymentDetail.setInvTotOtherDebitAmount(zero);
498 }
499
500 if (paymentDetail.getInvTotOtherCreditAmount() == null) {
501 paymentDetail.setInvTotOtherCreditAmount(zero);
502 }
503
504 // update the total payment amount with the amount from the accounts if null
505 if (paymentDetail.getNetPaymentAmount() == null) {
506 paymentDetail.setNetPaymentAmount(paymentDetail.getAccountTotal());
507 }
508
509 if (paymentDetail.getOrigInvoiceAmount() == null) {
510 KualiDecimal amt = paymentDetail.getNetPaymentAmount();
511 amt = amt.add(paymentDetail.getInvTotDiscountAmount());
512 amt = amt.subtract(paymentDetail.getInvTotShipAmount());
513 amt = amt.subtract(paymentDetail.getInvTotOtherDebitAmount());
514 amt = amt.add(paymentDetail.getInvTotOtherCreditAmount());
515 paymentDetail.setOrigInvoiceAmount(amt);
516 }
517 }
518
519 /**
520 * Sets null indicators to false
521 *
522 * @param paymentGroup <code>PaymentGroup</code> to update
523 */
524 protected void defaultGroupIndicators(PaymentGroup paymentGroup) {
525 // combineGroups column does not accept null values, so it will never be null
526 /*
527 * if (paymentGroup.getCombineGroups() == null) { paymentGroup.setCombineGroups(Boolean.TRUE); }
528 */
529
530 if (paymentGroup.getCampusAddress() == null) {
531 paymentGroup.setCampusAddress(Boolean.FALSE);
532 }
533
534 if (paymentGroup.getPymtAttachment() == null) {
535 paymentGroup.setPymtAttachment(Boolean.FALSE);
536 }
537
538 if (paymentGroup.getPymtSpecialHandling() == null) {
539 paymentGroup.setPymtSpecialHandling(Boolean.FALSE);
540 }
541
542 if (paymentGroup.getProcessImmediate() == null) {
543 paymentGroup.setProcessImmediate(Boolean.FALSE);
544 }
545
546 if (paymentGroup.getEmployeeIndicator() == null) {
547 paymentGroup.setEmployeeIndicator(Boolean.FALSE);
548 }
549
550 if (paymentGroup.getNraPayment() == null) {
551 paymentGroup.setNraPayment(Boolean.FALSE);
552 }
553
554 if (paymentGroup.getTaxablePayment() == null) {
555 paymentGroup.setTaxablePayment(Boolean.FALSE);
556 }
557 }
558
559 /**
560 * Checks whether payment status should be set to held and a tax email sent indicating so
561 *
562 * @param paymentFile payment file object
563 * @param paymentGroup <code>PaymentGroup</code> being checked
564 * @param customer payment customer
565 */
566 protected void checkForTaxEmailRequired(PaymentFileLoad paymentFile, PaymentGroup paymentGroup, CustomerProfile customer) {
567 PaymentStatus heldForNRAEmployee = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_EMPL_CD);
568 PaymentStatus heldForEmployee = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.HELD_TAX_EMPLOYEE_CD);
569 PaymentStatus heldForNRA = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_CD);
570
571 if (customer.getNraReview() && customer.getEmployeeCheck() && paymentGroup.getEmployeeIndicator().booleanValue() && paymentGroup.getNraPayment().booleanValue()) {
572 paymentGroup.setPaymentStatus(heldForNRAEmployee);
573 paymentFile.setTaxEmailRequired(true);
574 }
575
576 else if (customer.getEmployeeCheck() && paymentGroup.getEmployeeIndicator().booleanValue()) {
577 paymentGroup.setPaymentStatus(heldForEmployee);
578 paymentFile.setTaxEmailRequired(true);
579 }
580
581 else if (customer.getNraReview() && paymentGroup.getNraPayment().booleanValue()) {
582 paymentGroup.setPaymentStatus(heldForNRA);
583 paymentFile.setTaxEmailRequired(true);
584 }
585 }
586
587 /**
588 * Checks the payment date is not more than 30 days past or 30 days coming
589 *
590 * @param paymentGroup <code>PaymentGroup</code> being checked
591 * @param warnings <code>List</code> list of accumulated warning messages
592 */
593 protected void checkGroupPaymentDate(PaymentGroup paymentGroup, List<String> warnings) {
594 Timestamp now = dateTimeService.getCurrentTimestamp();
595
596 Calendar nowPlus30 = Calendar.getInstance();
597 nowPlus30.setTime(now);
598 nowPlus30.add(Calendar.DATE, 30);
599
600 Calendar nowMinus30 = Calendar.getInstance();
601 nowMinus30.setTime(now);
602 nowMinus30.add(Calendar.DATE, -30);
603
604 if (paymentGroup.getPaymentDate() != null) {
605 Calendar payDate = Calendar.getInstance();
606 payDate.setTime(paymentGroup.getPaymentDate());
607
608 if (payDate.before(nowMinus30)) {
609 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_PAYDATE_OVER_30_DAYS_PAST, dateTimeService.toDateString(paymentGroup.getPaymentDate()));
610 }
611
612 if (payDate.after(nowPlus30)) {
613 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_PAYDATE_OVER_30_DAYS_OUT, dateTimeService.toDateString(paymentGroup.getPaymentDate()));
614 }
615 }
616 else {
617 try {
618 paymentGroup.setPaymentDate(dateTimeService.convertToSqlDate(now));
619 }
620 catch (ParseException e) {
621 throw new RuntimeException("Unable to parse current timestamp into sql date " + e.getMessage());
622 }
623 }
624 }
625
626 /**
627 * @return system parameter value giving the maximum number of notes allowed.
628 */
629 protected int getMaxNoteLines() {
630 String maxLines = parameterService.getParameterValue(KfsParameterConstants.PRE_DISBURSEMENT_ALL.class, PdpParameterConstants.MAX_NOTE_LINES);
631 if (StringUtils.isBlank(maxLines)) {
632 throw new RuntimeException("System parameter for max note lines is blank");
633 }
634
635 return Integer.parseInt(maxLines);
636 }
637
638 /**
639 * Helper method for subsituting message parameters and adding the message to the warning list.
640 *
641 * @param warnings <code>List</code> of messages to add to
642 * @param messageKey resource key for message
643 * @param arguments message substitute parameters
644 */
645 protected void addWarningMessage(List<String> warnings, String messageKey, String... arguments) {
646 String message = kualiConfigurationService.getPropertyString(messageKey);
647 warnings.add(MessageFormat.format(message, (Object[]) arguments));
648 }
649
650 /**
651 * Sets the customerProfileService attribute value.
652 *
653 * @param customerProfileService The customerProfileService to set.
654 */
655 public void setCustomerProfileService(CustomerProfileService customerProfileService) {
656 this.customerProfileService = customerProfileService;
657 }
658
659 /**
660 * Sets the paymentFileLoadDao attribute value.
661 *
662 * @param paymentFileLoadDao The paymentFileLoadDao to set.
663 */
664 public void setPaymentFileLoadDao(PaymentFileLoadDao paymentFileLoadDao) {
665 this.paymentFileLoadDao = paymentFileLoadDao;
666 }
667
668 /**
669 * Sets the parameterService attribute value.
670 *
671 * @param parameterService The parameterService to set.
672 */
673 public void setParameterService(ParameterService parameterService) {
674 this.parameterService = parameterService;
675 }
676
677 /**
678 * Sets the dateTimeService attribute value.
679 *
680 * @param dateTimeService The dateTimeService to set.
681 */
682 public void setDateTimeService(DateTimeService dateTimeService) {
683 this.dateTimeService = dateTimeService;
684 }
685
686 /**
687 * Sets the accountService attribute value.
688 *
689 * @param accountService The accountService to set.
690 */
691 public void setAccountService(AccountService accountService) {
692 this.accountService = accountService;
693 }
694
695 /**
696 * Sets the subAccountService attribute value.
697 *
698 * @param subAccountService The subAccountService to set.
699 */
700 public void setSubAccountService(SubAccountService subAccountService) {
701 this.subAccountService = subAccountService;
702 }
703
704 /**
705 * Sets the objectCodeService attribute value.
706 *
707 * @param objectCodeService The objectCodeService to set.
708 */
709 public void setObjectCodeService(ObjectCodeService objectCodeService) {
710 this.objectCodeService = objectCodeService;
711 }
712
713 /**
714 * Sets the subObjectCodeService attribute value.
715 *
716 * @param subObjectCodeService The subObjectCodeService to set.
717 */
718 public void setSubObjectCodeService(SubObjectCodeService subObjectCodeService) {
719 this.subObjectCodeService = subObjectCodeService;
720 }
721
722 /**
723 * Sets the projectCodeService attribute value.
724 *
725 * @param projectCodeService The projectCodeService to set.
726 */
727 public void setProjectCodeService(ProjectCodeService projectCodeService) {
728 this.projectCodeService = projectCodeService;
729 }
730
731 /**
732 * Sets the kualiConfigurationService attribute value.
733 *
734 * @param kualiConfigurationService The kualiConfigurationService to set.
735 */
736 public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) {
737 this.kualiConfigurationService = kualiConfigurationService;
738 }
739
740 /**
741 * Sets the bankService attribute value.
742 *
743 * @param bankService The bankService to set.
744 */
745 public void setBankService(BankService bankService) {
746 this.bankService = bankService;
747 }
748
749 /**
750 * Sets the originationCodeService attribute value.
751 *
752 * @param originationCodeService The originationCodeService to set.
753 */
754 public void setOriginationCodeService(OriginationCodeService originationCodeService) {
755 this.originationCodeService = originationCodeService;
756 }
757
758 /**
759 * Gets the workflowInfoService attribute.
760 *
761 * @return Returns the workflowInfoService.
762 */
763 protected KualiWorkflowInfo getWorkflowInfoService() {
764 return workflowInfoService;
765 }
766
767 /**
768 * Sets the workflowInfoService attribute value.
769 *
770 * @param workflowInfoService The workflowInfoService to set.
771 */
772 public void setWorkflowInfoService(KualiWorkflowInfo workflowInfoService) {
773 this.workflowInfoService = workflowInfoService;
774 }
775
776 /**
777 * Gets the businessObjectService attribute.
778 *
779 * @return Returns the businessObjectService.
780 */
781 protected BusinessObjectService getBusinessObjectService() {
782 return businessObjectService;
783 }
784
785 /**
786 * Sets the businessObjectService attribute value.
787 *
788 * @param businessObjectService The businessObjectService to set.
789 */
790 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
791 this.businessObjectService = businessObjectService;
792 }
793
794 }