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    /*
017     * Created on Aug 12, 2004
018     */
019    package org.kuali.kfs.pdp.service.impl;
020    
021    import java.sql.Timestamp;
022    import java.util.Date;
023    import java.util.HashMap;
024    import java.util.List;
025    import java.util.Map;
026    
027    import org.kuali.kfs.pdp.PdpConstants;
028    import org.kuali.kfs.pdp.PdpKeyConstants;
029    import org.kuali.kfs.pdp.PdpPropertyConstants;
030    import org.kuali.kfs.pdp.businessobject.AchAccountNumber;
031    import org.kuali.kfs.pdp.businessobject.PaymentChangeCode;
032    import org.kuali.kfs.pdp.businessobject.PaymentDetail;
033    import org.kuali.kfs.pdp.businessobject.PaymentGroup;
034    import org.kuali.kfs.pdp.businessobject.PaymentGroupHistory;
035    import org.kuali.kfs.pdp.businessobject.PaymentNoteText;
036    import org.kuali.kfs.pdp.businessobject.PaymentStatus;
037    import org.kuali.kfs.pdp.dataaccess.PaymentDetailDao;
038    import org.kuali.kfs.pdp.dataaccess.PaymentGroupDao;
039    import org.kuali.kfs.pdp.service.EnvironmentService;
040    import org.kuali.kfs.pdp.service.PaymentGroupService;
041    import org.kuali.kfs.pdp.service.PaymentMaintenanceService;
042    import org.kuali.kfs.pdp.service.PdpAuthorizationService;
043    import org.kuali.kfs.pdp.service.PdpEmailService;
044    import org.kuali.kfs.pdp.service.PendingTransactionService;
045    import org.kuali.kfs.sys.KFSConstants;
046    import org.kuali.kfs.sys.service.BankService;
047    import org.kuali.rice.kim.bo.Person;
048    import org.kuali.rice.kns.bo.KualiCode;
049    import org.kuali.rice.kns.service.BusinessObjectService;
050    import org.kuali.rice.kns.service.MailService;
051    import org.kuali.rice.kns.service.ParameterService;
052    import org.kuali.rice.kns.util.GlobalVariables;
053    import org.kuali.rice.kns.util.KualiInteger;
054    import org.kuali.rice.kns.util.ObjectUtils;
055    import org.springframework.transaction.annotation.Transactional;
056    
057    /**
058     * @see org.kuali.kfs.pdp.service.PaymentMaintenanceService
059     */
060    @Transactional
061    public class PaymentMaintenanceServiceImpl implements PaymentMaintenanceService {
062        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentMaintenanceServiceImpl.class);
063    
064        private PaymentGroupDao paymentGroupDao;
065        private PaymentDetailDao paymentDetailDao;
066        private PendingTransactionService glPendingTransactionService;
067        private EnvironmentService environmentService;
068        private MailService mailService;
069        private ParameterService parameterService;
070        private BankService bankService;
071        private BusinessObjectService businessObjectService;
072        private PaymentGroupService paymentGroupService;
073        private PdpEmailService emailService;
074        private PdpAuthorizationService pdpAuthorizationService;
075    
076        /**
077         * This method changes status for a payment group.
078         * 
079         * @param paymentGroup the payment group
080         * @param newPaymentStatus the new payment status
081         * @param changeStatus the changed payment status
082         * @param note a note for payment status change
083         * @param user the user that changed the status
084         */
085        protected void changeStatus(PaymentGroup paymentGroup, String newPaymentStatus, String changeStatus, String note, Person user) {
086            if (LOG.isDebugEnabled()) {
087                LOG.debug("changeStatus() enter method with new status of " + newPaymentStatus);
088            }
089    
090            PaymentGroupHistory paymentGroupHistory = new PaymentGroupHistory();
091            KualiCode cd = businessObjectService.findBySinglePrimaryKey(PaymentChangeCode.class, changeStatus);
092            paymentGroupHistory.setPaymentChange((PaymentChangeCode) cd);
093            paymentGroupHistory.setOrigPaymentStatus(paymentGroup.getPaymentStatus());
094            paymentGroupHistory.setChangeUser(user);
095            paymentGroupHistory.setChangeNoteText(note);
096            paymentGroupHistory.setPaymentGroup(paymentGroup);
097            paymentGroupHistory.setChangeTime(new Timestamp(new Date().getTime()));
098    
099            this.businessObjectService.save(paymentGroupHistory);
100    
101            KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, newPaymentStatus);
102            paymentGroup.setPaymentStatus((PaymentStatus) code);
103            this.businessObjectService.save(paymentGroup);
104            LOG.debug("changeStatus() Status has been changed; exit method.");
105        }
106    
107        /**
108         * This method changes the state of a paymentGroup.
109         * 
110         * @param paymentGroup the payment group to change the state for
111         * @param newPaymentStatus the new payment status
112         * @param changeStatus the status that is changed
113         * @param note the note entered by the user
114         * @param user the user that changed the
115         * @param paymentGroupHistory
116         */
117        protected void changeStatus(PaymentGroup paymentGroup, String newPaymentStatus, String changeStatus, String note, Person user, PaymentGroupHistory paymentGroupHistory) {
118            if (LOG.isDebugEnabled()) {
119                LOG.debug("changeStatus() enter method with new status of " + newPaymentStatus);
120            }
121    
122            KualiCode cd = businessObjectService.findBySinglePrimaryKey(PaymentChangeCode.class, changeStatus);
123            paymentGroupHistory.setPaymentChange((PaymentChangeCode) cd);
124            paymentGroupHistory.setOrigPaymentStatus(paymentGroup.getPaymentStatus());
125            paymentGroupHistory.setChangeUser(user);
126            paymentGroupHistory.setChangeNoteText(note);
127            paymentGroupHistory.setPaymentGroup(paymentGroup);
128            paymentGroupHistory.setChangeTime(new Timestamp(new Date().getTime()));
129    
130            this.businessObjectService.save(paymentGroupHistory);
131    
132            KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, newPaymentStatus);
133            if (paymentGroup.getPaymentStatus() != ((PaymentStatus) code)) {
134                paymentGroup.setPaymentStatus((PaymentStatus) code);
135            }
136            this.businessObjectService.save(paymentGroup);
137    
138            LOG.debug("changeStatus() Status has been changed; exit method.");
139        }
140    
141        /**
142         * @see org.kuali.kfs.pdp.document.service.PaymentMaintenanceService#cancelPendingPayment(java.lang.Integer, java.lang.Integer,
143         *      java.lang.String, org.kuali.rice.kim.bo.Person)
144         */
145        public boolean cancelPendingPayment(Integer paymentGroupId, Integer paymentDetailId, String note, Person user) {
146            // All actions must be performed on entire group not individual detail record
147            if (LOG.isDebugEnabled()) {
148                LOG.debug("cancelPendingPayment() Enter method to cancel pending payment with group id = " + paymentGroupId);
149                LOG.debug("cancelPendingPayment() payment detail id being cancelled = " + paymentDetailId);
150            }
151    
152            PaymentGroup paymentGroup = this.paymentGroupService.get(paymentGroupId);
153            if (paymentGroup == null) {
154                LOG.debug("cancelPendingPayment() Pending payment not found; throw exception.");
155                GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_PAYMENT_NOT_FOUND);
156                return false;
157            }
158    
159            String paymentStatus = paymentGroup.getPaymentStatus().getCode();
160    
161            if (!(PdpConstants.PaymentStatusCodes.CANCEL_PAYMENT.equals(paymentStatus))) {
162                if (LOG.isDebugEnabled()) {
163                    LOG.debug("cancelPendingPayment() Payment status is " + paymentStatus + "; continue with cancel.");
164                }
165    
166                if ((PdpConstants.PaymentStatusCodes.HELD_TAX_EMPLOYEE_CD.equals(paymentStatus)) || (PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_CD.equals(paymentStatus)) || (PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_EMPL_CD.equals(paymentStatus))) {
167                    if (!pdpAuthorizationService.hasRemovePaymentTaxHoldPermission(user.getPrincipalId())) {
168                        LOG.warn("cancelPendingPayment() Payment status is " + paymentStatus + "; user does not have rights to cancel. This should not happen unless user is URL spoofing.");
169                        throw new RuntimeException("cancelPendingPayment() Payment status is " + paymentStatus + "; user does not have rights to cancel. This should not happen unless user is URL spoofing.");
170                    }
171    
172                    changeStatus(paymentGroup, PdpConstants.PaymentStatusCodes.CANCEL_PAYMENT, PdpConstants.PaymentChangeCodes.CANCEL_PAYMENT_CHNG_CD, note, user);
173    
174                    // set primary cancel indicator for EPIC to use
175                    Map primaryKeys = new HashMap();
176                    primaryKeys.put(PdpPropertyConstants.PaymentDetail.PAYMENT_ID, paymentDetailId);
177    
178                    PaymentDetail pd = (PaymentDetail) this.businessObjectService.findByPrimaryKey(PaymentDetail.class, primaryKeys);
179                    if (pd != null) {
180                        pd.setPrimaryCancelledPayment(Boolean.TRUE);
181                    }
182                    this.businessObjectService.save(pd);
183                    this.emailService.sendCancelEmail(paymentGroup, note, user);
184    
185                    LOG.debug("cancelPendingPayment() Pending payment cancelled and mail was sent; exit method.");
186                }
187                else if (PdpConstants.PaymentStatusCodes.OPEN.equals(paymentStatus) || PdpConstants.PaymentStatusCodes.HELD_CD.equals(paymentStatus)) {
188                    if (!pdpAuthorizationService.hasCancelPaymentPermission(user.getPrincipalId())) {
189                        LOG.warn("cancelPendingPayment() Payment status is " + paymentStatus + "; user does not have rights to cancel. This should not happen unless user is URL spoofing.");
190                        throw new RuntimeException("cancelPendingPayment() Payment status is " + paymentStatus + "; user does not have rights to cancel. This should not happen unless user is URL spoofing.");
191                    }
192    
193                    changeStatus(paymentGroup, PdpConstants.PaymentStatusCodes.CANCEL_PAYMENT, PdpConstants.PaymentChangeCodes.CANCEL_PAYMENT_CHNG_CD, note, user);
194    
195                    // set primary cancel indicator for EPIC to use
196                    Map primaryKeys = new HashMap();
197                    primaryKeys.put(PdpPropertyConstants.PaymentDetail.PAYMENT_ID, paymentDetailId);
198    
199                    PaymentDetail pd = (PaymentDetail) this.businessObjectService.findByPrimaryKey(PaymentDetail.class, primaryKeys);
200                    if (pd != null) {
201                        pd.setPrimaryCancelledPayment(Boolean.TRUE);
202                        PaymentNoteText payNoteText = new PaymentNoteText();
203                        payNoteText.setCustomerNoteLineNbr(new KualiInteger(pd.getNotes().size() + 1));
204                        payNoteText.setCustomerNoteText(note);
205                        pd.addNote(payNoteText);
206                    }
207    
208                    this.businessObjectService.save(pd);
209    
210                    LOG.debug("cancelPendingPayment() Pending payment cancelled; exit method.");
211                }
212                else {
213                    if (LOG.isDebugEnabled()) {
214                        LOG.debug("cancelPendingPayment() Payment status is " + paymentStatus + "; cannot cancel payment in this status");
215                    }
216    
217                    GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_PAYMENT_INVALID_STATUS_TO_CANCEL);
218                    return false;
219                }
220            }
221            else {
222                LOG.debug("cancelPendingPayment() Pending payment group has already been cancelled; exit method.");
223            }
224            return true;
225        }
226    
227        /**
228         * @see org.kuali.kfs.pdp.document.service.PaymentMaintenanceService#holdPendingPayment(java.lang.Integer, java.lang.String,
229         *      org.kuali.rice.kim.bo.Person)
230         */
231        public boolean holdPendingPayment(Integer paymentGroupId, String note, Person user) {
232            // All actions must be performed on entire group not individual detail record
233            if (LOG.isDebugEnabled()) {
234                LOG.debug("holdPendingPayment() Enter method to hold pending payment with id = " + paymentGroupId);
235            }
236    
237            if (!pdpAuthorizationService.hasHoldPaymentPermission(user.getPrincipalId())) {
238                LOG.warn("holdPendingPayment() User " + user.getPrincipalId() + " does not have rights to hold payments. This should not happen unless user is URL spoofing.");
239                throw new RuntimeException("holdPendingPayment() User " + user.getPrincipalId() + " does not have rights to hold payments. This should not happen unless user is URL spoofing.");
240            }
241    
242            PaymentGroup paymentGroup = this.paymentGroupService.get(paymentGroupId);
243            if (paymentGroup == null) {
244                LOG.debug("holdPendingPayment() Pending payment not found; throw exception.");
245                GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_PAYMENT_NOT_FOUND);
246                return false;
247            }
248    
249            String paymentStatus = paymentGroup.getPaymentStatus().getCode();
250    
251            if (!(PdpConstants.PaymentStatusCodes.HELD_CD.equals(paymentStatus))) {
252                if (PdpConstants.PaymentStatusCodes.OPEN.equals(paymentStatus)) {
253                    if (LOG.isDebugEnabled()) {
254                        LOG.debug("holdPendingPayment() Payment status is " + paymentStatus + "; continue with hold.");
255                    }
256    
257                    changeStatus(paymentGroup, PdpConstants.PaymentStatusCodes.HELD_CD, PdpConstants.PaymentChangeCodes.HOLD_CHNG_CD, note, user);
258    
259                    LOG.debug("holdPendingPayment() Pending payment was put on hold; exit method.");
260                }
261                else {
262                    if (LOG.isDebugEnabled()) {
263                        LOG.debug("holdPendingPayment() Payment status is " + paymentStatus + "; cannot hold payment in this status");
264                    }
265    
266                    GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_PAYMENT_INVALID_STATUS_TO_HOLD);
267                    return false;
268                }
269            }
270            else {
271                LOG.debug("holdPendingPayment() Pending payment group has already been held; exit method.");
272            }
273            return true;
274    
275        }
276    
277        /**
278         * @see org.kuali.kfs.pdp.document.service.PaymentMaintenanceService#removeHoldPendingPayment(java.lang.Integer,
279         *      java.lang.String, org.kuali.rice.kim.bo.Person)
280         */
281        public boolean removeHoldPendingPayment(Integer paymentGroupId, String note, Person user) {
282            // All actions must be performed on entire group not individual detail record
283            if (LOG.isDebugEnabled()) {
284                LOG.debug("removeHoldPendingPayment() Enter method to hold pending payment with id = " + paymentGroupId);
285            }
286            PaymentGroup paymentGroup = this.paymentGroupService.get(paymentGroupId);
287            if (paymentGroup == null) {
288                LOG.debug("removeHoldPendingPayment() Payment not found; throw exception.");
289    
290                GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_PAYMENT_NOT_FOUND);
291                return false;
292            }
293    
294            String paymentStatus = paymentGroup.getPaymentStatus().getCode();
295    
296            if (!(PdpConstants.PaymentStatusCodes.OPEN.equals(paymentStatus))) {
297                if (LOG.isDebugEnabled()) {
298                    LOG.debug("removeHoldPendingPayment() Payment status is " + paymentStatus + "; continue with hold removal.");
299                }
300    
301                if ((PdpConstants.PaymentStatusCodes.HELD_TAX_EMPLOYEE_CD.equals(paymentStatus)) || (PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_CD.equals(paymentStatus)) || (PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_EMPL_CD.equals(paymentStatus))) {
302                    if (!pdpAuthorizationService.hasRemovePaymentTaxHoldPermission(user.getPrincipalId())) {
303                        LOG.warn("removeHoldPendingPayment() User " + user.getPrincipalId() + " does not have rights to remove tax holds. This should not happen unless user is URL spoofing.");
304                        throw new RuntimeException("removeHoldPendingPayment() User " + user.getPrincipalId() + " does not have rights to remove tax holds. This should not happen unless user is URL spoofing.");
305                    }
306    
307                    changeStatus(paymentGroup, PdpConstants.PaymentStatusCodes.OPEN, PdpConstants.PaymentChangeCodes.REMOVE_HOLD_CHNG_CD, note, user);
308                    LOG.debug("removeHoldPendingPayment() Pending payment was taken off hold; exit method.");
309                }
310                else if (PdpConstants.PaymentStatusCodes.HELD_CD.equals(paymentStatus)) {
311                    if (!pdpAuthorizationService.hasHoldPaymentPermission(user.getPrincipalId())) {
312                        LOG.warn("removeHoldPendingPayment() User " + user.getPrincipalId() + " does not have rights to hold payments. This should not happen unless user is URL spoofing.");
313                        throw new RuntimeException("removeHoldPendingPayment() User " + user.getPrincipalId() + " does not have rights to hold payments. This should not happen unless user is URL spoofing.");
314                    }
315    
316                    changeStatus(paymentGroup, PdpConstants.PaymentStatusCodes.OPEN, PdpConstants.PaymentChangeCodes.REMOVE_HOLD_CHNG_CD, note, user);
317    
318                    LOG.debug("removeHoldPendingPayment() Pending payment was taken off hold; exit method.");
319                }
320                else {
321                    if (LOG.isDebugEnabled()) {
322                        LOG.debug("removeHoldPendingPayment() Payment status is " + paymentStatus + "; cannot remove hold on payment in this status");
323                    }
324    
325                    GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_PAYMENT_INVALID_STATUS_TO_REMOVE_HOLD);
326                    return false;
327                }
328            }
329            else {
330                LOG.debug("removeHoldPendingPayment() Pending payment group has already been un-held; exit method.");
331            }
332            return true;
333        }
334    
335        /**
336         * @see org.kuali.kfs.pdp.document.service.PaymentMaintenanceService#changeImmediateFlag(java.lang.Integer, java.lang.String,
337         *      org.kuali.rice.kim.bo.Person)
338         */
339        public void changeImmediateFlag(Integer paymentGroupId, String note, Person user) {
340            // All actions must be performed on entire group not individual detail record
341            if (LOG.isDebugEnabled()) {
342                LOG.debug("changeImmediateFlag() Enter method to hold pending payment with id = " + paymentGroupId);
343            }
344            
345            if (!pdpAuthorizationService.hasSetAsImmediatePayPermission(user.getPrincipalId())) {
346                LOG.warn("changeImmediateFlag() User " + user.getPrincipalId() + " does not have rights to set payments as immediate. This should not happen unless user is URL spoofing.");
347                throw new RuntimeException("changeImmediateFlag() User " + user.getPrincipalId() + " does not have rights to payments as immediate. This should not happen unless user is URL spoofing.");
348            }
349            
350            PaymentGroupHistory paymentGroupHistory = new PaymentGroupHistory();
351            PaymentGroup paymentGroup = this.paymentGroupService.get(paymentGroupId);
352    
353            paymentGroupHistory.setOrigProcessImmediate(paymentGroup.getProcessImmediate());
354    
355            if (paymentGroup.getProcessImmediate().equals(Boolean.TRUE)) {
356                paymentGroup.setProcessImmediate(Boolean.FALSE);
357            }
358            else {
359                paymentGroup.setProcessImmediate(Boolean.TRUE);
360            }
361    
362            changeStatus(paymentGroup, paymentGroup.getPaymentStatus().getCode(), PdpConstants.PaymentChangeCodes.CHANGE_IMMEDIATE_CHNG_CD, note, user, paymentGroupHistory);
363    
364            LOG.debug("changeImmediateFlag() exit method.");
365        }
366    
367        /**
368         * @see org.kuali.kfs.pdp.document.service.PaymentMaintenanceService#cancelDisbursement(java.lang.Integer, java.lang.Integer,
369         *      java.lang.String, org.kuali.rice.kim.bo.Person)
370         */
371        public boolean cancelDisbursement(Integer paymentGroupId, Integer paymentDetailId, String note, Person user) {
372            // All actions must be performed on entire group not individual detail record
373            if (LOG.isDebugEnabled()) {
374                LOG.debug("cancelDisbursement() Enter method to cancel disbursement with id = " + paymentGroupId);
375            }
376            
377            if (!pdpAuthorizationService.hasCancelPaymentPermission(user.getPrincipalId())) {
378                LOG.warn("cancelDisbursement() User " + user.getPrincipalId() + " does not have rights to cancel payments. This should not happen unless user is URL spoofing.");
379                throw new RuntimeException("cancelDisbursement() User " + user.getPrincipalId() + " does not have rights to cancel payments. This should not happen unless user is URL spoofing.");
380            }
381    
382            PaymentGroup paymentGroup = this.paymentGroupService.get(paymentGroupId);
383    
384            if (paymentGroup == null) {
385                LOG.debug("cancelDisbursement() Disbursement not found; throw exception.");
386                GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_NOT_FOUND);
387                return false;
388            }
389    
390            String paymentStatus = paymentGroup.getPaymentStatus().getCode();
391    
392            if (!(PdpConstants.PaymentStatusCodes.CANCEL_DISBURSEMENT.equals(paymentStatus))) {
393                if (((PdpConstants.PaymentStatusCodes.EXTRACTED.equals(paymentStatus)) && (ObjectUtils.isNotNull(paymentGroup.getDisbursementDate()))) || (PdpConstants.PaymentStatusCodes.PENDING_ACH.equals(paymentStatus))) {
394                    if (LOG.isDebugEnabled()) {
395                        LOG.debug("cancelDisbursement() Payment status is " + paymentStatus + "; continue with cancel.");
396                    }
397    
398                    List<PaymentGroup> allDisbursementPaymentGroups = this.paymentGroupService.getByDisbursementNumber(paymentGroup.getDisbursementNbr().intValue());
399    
400                    for (PaymentGroup element : allDisbursementPaymentGroups) {
401                        PaymentGroupHistory pgh = new PaymentGroupHistory();
402                        
403                        if (!element.getPaymentDetails().get(0).isDisbursementActionAllowed()) {
404                            LOG.warn("cancelDisbursement() Payment does not allow disbursement action. This should not happen unless user is URL spoofing.");
405                            throw new RuntimeException("cancelDisbursement() Payment does not allow disbursement action. This should not happen unless user is URL spoofing.");                       
406                        }
407    
408                        if ((ObjectUtils.isNotNull(element.getDisbursementType())) && (element.getDisbursementType().getCode().equals(PdpConstants.DisbursementTypeCodes.CHECK))) {
409                            pgh.setPmtCancelExtractStat(Boolean.FALSE);
410                        }
411    
412                        changeStatus(element, PdpConstants.PaymentStatusCodes.CANCEL_DISBURSEMENT, PdpConstants.PaymentChangeCodes.CANCEL_DISBURSEMENT, note, user, pgh);
413    
414                        glPendingTransactionService.generateCancellationGeneralLedgerPendingEntry(element);
415                    }
416    
417                    // set primary cancel indicator for EPIC to use
418                    Map primaryKeys = new HashMap();
419                    primaryKeys.put(PdpPropertyConstants.PaymentDetail.PAYMENT_ID, paymentDetailId);
420    
421                    PaymentDetail pd = (PaymentDetail) this.businessObjectService.findByPrimaryKey(PaymentDetail.class, primaryKeys);
422                    if (pd != null) {
423                        pd.setPrimaryCancelledPayment(Boolean.TRUE);
424                    }
425    
426                    this.businessObjectService.save(pd);
427    
428                    LOG.debug("cancelDisbursement() Disbursement cancelled; exit method.");
429                }
430                else {
431                    if (LOG.isDebugEnabled()) {
432                        LOG.debug("cancelDisbursement() Payment status is " + paymentStatus + " and disbursement date is " + paymentGroup.getDisbursementDate() + "; cannot cancel payment in this status");
433                    }
434    
435                    GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_INVALID_TO_CANCEL);
436                    return false;
437                }
438            }
439            else {
440                LOG.debug("cancelDisbursement() Disbursement has already been cancelled; exit method.");
441            }
442            return true;
443        }
444    
445        /**
446         * @see org.kuali.kfs.pdp.document.service.PaymentMaintenanceService#cancelReissueDisbursement(java.lang.Integer,
447         *      java.lang.String, org.kuali.rice.kim.bo.Person)
448         */
449        public boolean cancelReissueDisbursement(Integer paymentGroupId, String note, Person user) {
450            // All actions must be performed on entire group not individual detail record
451            if (LOG.isDebugEnabled()) {
452                LOG.debug("cancelReissueDisbursement() Enter method to cancel disbursement with id = " + paymentGroupId);
453            }
454            
455            if (!pdpAuthorizationService.hasCancelPaymentPermission(user.getPrincipalId())) {
456                LOG.warn("cancelReissueDisbursement() User " + user.getPrincipalId() + " does not have rights to cancel payments. This should not happen unless user is URL spoofing.");
457                throw new RuntimeException("cancelReissueDisbursement() User " + user.getPrincipalId() + " does not have rights to cancel payments. This should not happen unless user is URL spoofing.");
458            }
459    
460            PaymentGroup paymentGroup = this.paymentGroupService.get(paymentGroupId);
461            if (paymentGroup == null) {
462                LOG.debug("cancelReissueDisbursement() Disbursement not found; throw exception.");
463                GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_NOT_FOUND);
464                return false;
465            }
466    
467            String paymentStatus = paymentGroup.getPaymentStatus().getCode();
468    
469            if (!(PdpConstants.PaymentStatusCodes.OPEN.equals(paymentStatus))) {
470                if (((PdpConstants.PaymentStatusCodes.EXTRACTED.equals(paymentStatus)) && (ObjectUtils.isNotNull(paymentGroup.getDisbursementDate()))) || (PdpConstants.PaymentStatusCodes.PENDING_ACH.equals(paymentStatus))) {
471                    if (LOG.isDebugEnabled()) {
472                        LOG.debug("cancelReissueDisbursement() Payment status is " + paymentStatus + "; continue with cancel.");
473                    }
474    
475                    List<PaymentGroup> allDisbursementPaymentGroups = this.paymentGroupService.getByDisbursementNumber(paymentGroup.getDisbursementNbr().intValue());
476    
477                    for (PaymentGroup pg : allDisbursementPaymentGroups) {
478                        PaymentGroupHistory pgh = new PaymentGroupHistory();
479                        
480                        if (!pg.getPaymentDetails().get(0).isDisbursementActionAllowed()) {
481                            LOG.warn("cancelDisbursement() Payment does not allow disbursement action. This should not happen unless user is URL spoofing.");
482                            throw new RuntimeException("cancelDisbursement() Payment does not allow disbursement action. This should not happen unless user is URL spoofing.");                       
483                        }
484    
485                        if ((ObjectUtils.isNotNull(pg.getDisbursementType())) && (pg.getDisbursementType().getCode().equals(PdpConstants.DisbursementTypeCodes.CHECK))) {
486                            pgh.setPmtCancelExtractStat(Boolean.FALSE);
487                        }
488    
489                        pgh.setOrigProcessImmediate(pg.getProcessImmediate());
490                        pgh.setOrigPmtSpecHandling(pg.getPymtSpecialHandling());
491                        pgh.setBank(pg.getBank());
492                        pgh.setOrigPaymentDate(pg.getPaymentDate());
493                        //put a check for null since disbursement date was not set in testMode / dev
494                        if (ObjectUtils.isNotNull(pg.getDisbursementDate())) {
495                            pgh.setOrigDisburseDate(new Timestamp(pg.getDisbursementDate().getTime()));
496                        }
497                        pgh.setOrigAchBankRouteNbr(pg.getAchBankRoutingNbr());
498                        pgh.setOrigDisburseNbr(pg.getDisbursementNbr());
499                        pgh.setOrigAdviceEmail(pg.getAdviceEmailAddress());
500                        pgh.setDisbursementType(pg.getDisbursementType());
501                        pgh.setProcess(pg.getProcess());
502    
503                        glPendingTransactionService.generateReissueGeneralLedgerPendingEntry(pg);
504    
505                        if (LOG.isDebugEnabled()) {
506                            LOG.debug("cancelReissueDisbursement() Status is '" + paymentStatus + "; delete row from AchAccountNumber table.");
507                        }
508    
509                        AchAccountNumber achAccountNumber = pg.getAchAccountNumber();
510    
511                        if (ObjectUtils.isNotNull(achAccountNumber)) {
512                            this.businessObjectService.delete(achAccountNumber);
513                            pg.setAchAccountNumber(null);
514                        }
515    
516                        // if bank functionality is not enabled or the group bank is inactive clear bank code
517                        if (!bankService.isBankSpecificationEnabled() || !pg.getBank().isActive()) {
518                            pg.setBank(null);
519                        }
520    
521                        pg.setDisbursementDate((java.sql.Date) null);
522                        pg.setAchBankRoutingNbr(null);
523                        pg.setAchAccountType(null);
524                        pg.setPhysCampusProcessCd(null);
525                        pg.setDisbursementNbr((KualiInteger) null);
526                        pg.setAdviceEmailAddress(null);
527                        pg.setDisbursementType(null);
528                        pg.setProcess(null);
529                        pg.setProcessImmediate(false);
530    
531                        changeStatus(pg, PdpConstants.PaymentStatusCodes.OPEN, PdpConstants.PaymentChangeCodes.CANCEL_REISSUE_DISBURSEMENT, note, user, pgh);
532                    }
533    
534                    LOG.debug("cancelReissueDisbursement() Disbursement cancelled and reissued; exit method.");
535                }
536                else {
537                    if (LOG.isDebugEnabled()) {
538                        LOG.debug("cancelReissueDisbursement() Payment status is " + paymentStatus + " and disbursement date is " + paymentGroup.getDisbursementDate() + "; cannot cancel payment");
539                    }
540    
541                    GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_INVALID_TO_CANCEL_AND_REISSUE);
542                    return false;
543                }
544            }
545            else {
546                LOG.debug("cancelReissueDisbursement() Disbursement already cancelled and reissued; exit method.");
547            }
548            return true;
549        }
550    
551        /**
552         * inject
553         * 
554         * @param dao
555         */
556        public void setPaymentGroupDao(PaymentGroupDao dao) {
557            paymentGroupDao = dao;
558        }
559    
560        /**
561         * inject
562         * 
563         * @param dao
564         */
565        public void setPaymentDetailDao(PaymentDetailDao dao) {
566            paymentDetailDao = dao;
567        }
568    
569        /**
570         * inject
571         * 
572         * @param service
573         */
574        public void setGlPendingTransactionService(PendingTransactionService service) {
575            glPendingTransactionService = service;
576        }
577    
578        /**
579         * inject
580         * 
581         * @param service
582         */
583        public void setEnvironmentService(EnvironmentService environmentService) {
584            this.environmentService = environmentService;
585        }
586    
587        /**
588         * inject
589         * 
590         * @param service
591         */
592        public void setMailService(MailService mailService) {
593            this.mailService = mailService;
594        }
595    
596        public void setParameterService(ParameterService parameterService) {
597            this.parameterService = parameterService;
598        }
599    
600        /**
601         * Sets the bankService attribute value.
602         * 
603         * @param bankService The bankService to set.
604         */
605        public void setBankService(BankService bankService) {
606            this.bankService = bankService;
607        }
608    
609        /**
610         * Sets the business object service
611         * 
612         * @param businessObjectService
613         */
614        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
615            this.businessObjectService = businessObjectService;
616        }
617    
618        /**
619         * Sets the payment group service
620         * 
621         * @param paymentGroupService
622         */
623        public void setPaymentGroupService(PaymentGroupService paymentGroupService) {
624            this.paymentGroupService = paymentGroupService;
625        }
626    
627        public void setEmailService(PdpEmailService emailService) {
628            this.emailService = emailService;
629        }
630    
631        public void setPdpAuthorizationService(PdpAuthorizationService pdpAuthorizationService) {
632            this.pdpAuthorizationService = pdpAuthorizationService;
633        }
634    }