001/*
002 * The Kuali Financial System, a comprehensive financial management system for higher education.
003 * 
004 * Copyright 2005-2014 The Kuali Foundation
005 * 
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU Affero General Public License as
008 * published by the Free Software Foundation, either version 3 of the
009 * License, or (at your option) any later version.
010 * 
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU Affero General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Affero General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.kuali.kfs.fp.document;
020
021import java.util.ArrayList;
022import java.util.List;
023
024import org.kuali.kfs.fp.businessobject.AdvanceDepositDetail;
025import org.kuali.kfs.sys.KFSConstants;
026import org.kuali.kfs.sys.KFSKeyConstants;
027import org.kuali.kfs.sys.KFSPropertyConstants;
028import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
029import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
030import org.kuali.kfs.sys.context.SpringContext;
031import org.kuali.kfs.sys.document.AmountTotaling;
032import org.kuali.kfs.sys.document.Correctable;
033import org.kuali.kfs.sys.document.service.AccountingDocumentRuleHelperService;
034import org.kuali.kfs.sys.service.BankService;
035import org.kuali.kfs.sys.service.ElectronicPaymentClaimingService;
036import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
037import org.kuali.rice.core.api.util.type.KualiDecimal;
038import org.kuali.rice.core.web.format.CurrencyFormatter;
039import org.kuali.rice.kew.api.exception.WorkflowException;
040import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
041import org.kuali.rice.kns.service.DataDictionaryService;
042import org.kuali.rice.krad.document.Copyable;
043import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
044import org.kuali.rice.krad.rules.rule.event.SaveDocumentEvent;
045
046/**
047 * This is the business object that represents the AdvanceDeposit document in Kuali. This is a transactional document that will
048 * eventually post transactions to the G/L. It integrates with workflow. Since an Advance Deposit document is a one sided
049 * transactional document, only accepting funds into the university, the accounting line data will be held in the source accounting
050 * line data structure only.
051 */
052public class AdvanceDepositDocument extends CashReceiptFamilyBase implements Copyable, AmountTotaling, Correctable {
053    public static final String ADVANCE_DEPOSIT_DOCUMENT_TYPE_CODE = "AD";
054
055    // holds details about each advance deposit
056    protected List<AdvanceDepositDetail> advanceDeposits = new ArrayList<AdvanceDepositDetail>();
057
058    // incrementers for detail lines
059    protected Integer nextAdvanceDepositLineNumber = 1;
060
061    // monetary attributes
062    protected KualiDecimal totalAdvanceDepositAmount = KualiDecimal.ZERO;
063
064    /**
065     * Default constructor that calls super.
066     */
067    public AdvanceDepositDocument() {
068        super();
069    }
070
071    /**
072     * Gets the total advance deposit amount.
073     *
074     * @return KualiDecimal
075     */
076    public KualiDecimal getTotalAdvanceDepositAmount() {
077        return totalAdvanceDepositAmount;
078    }
079
080    /**
081     * This method returns the advance deposit total amount as a currency formatted string.
082     *
083     * @return String
084     */
085    public String getCurrencyFormattedTotalAdvanceDepositAmount() {
086        return (String) new CurrencyFormatter().format(totalAdvanceDepositAmount);
087    }
088
089    /**
090     * Sets the total advance deposit amount which is the sum of all advance deposits on this document.
091     *
092     * @param advanceDepositAmount
093     */
094    public void setTotalAdvanceDepositAmount(KualiDecimal advanceDepositAmount) {
095        this.totalAdvanceDepositAmount = advanceDepositAmount;
096    }
097
098    /**
099     * Gets the list of advance deposits which is a list of AdvanceDepositDetail business objects.
100     *
101     * @return List
102     */
103    public List<AdvanceDepositDetail> getAdvanceDeposits() {
104        return advanceDeposits;
105    }
106
107    /**
108     * Sets the advance deposits list.
109     *
110     * @param advanceDeposits
111     */
112    public void setAdvanceDeposits(List<AdvanceDepositDetail> advanceDeposits) {
113        this.advanceDeposits = advanceDeposits;
114    }
115
116    /**
117     * Adds a new advance deposit to the list.
118     *
119     * @param advanceDepositDetail
120     */
121    public void addAdvanceDeposit(AdvanceDepositDetail advanceDepositDetail) {
122        // these three make up the primary key for an advance deposit detail record
123        prepareNewAdvanceDeposit(advanceDepositDetail);
124
125        // add the new detail record to the list
126        this.advanceDeposits.add(advanceDepositDetail);
127
128        // increment line number
129        this.nextAdvanceDepositLineNumber++;
130
131        // update the overall amount
132        this.totalAdvanceDepositAmount = this.totalAdvanceDepositAmount.add(advanceDepositDetail.getFinancialDocumentAdvanceDepositAmount());
133    }
134
135    /**
136     * This is a helper method that automatically populates document specfic information into the advance deposit
137     * (AdvanceDepositDetail) instance.
138     *
139     * @param advanceDepositDetail
140     */
141    public final void prepareNewAdvanceDeposit(AdvanceDepositDetail advanceDepositDetail) {
142        advanceDepositDetail.setFinancialDocumentLineNumber(this.nextAdvanceDepositLineNumber);
143        advanceDepositDetail.setDocumentNumber(this.getDocumentNumber());
144        advanceDepositDetail.setFinancialDocumentTypeCode(SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass()));
145    }
146
147    /**
148     * Retrieve a particular advance deposit at a given index in the list of advance deposits.
149     *
150     * @param index
151     * @return AdvanceDepositDetail
152     */
153    public AdvanceDepositDetail getAdvanceDepositDetail(int index) {
154        while (this.advanceDeposits.size() <= index) {
155            advanceDeposits.add(new AdvanceDepositDetail());
156        }
157        return advanceDeposits.get(index);
158    }
159
160    /**
161     * This method removes an advance deposit from the list and updates the total appropriately.
162     *
163     * @param index
164     */
165    public void removeAdvanceDeposit(int index) {
166        AdvanceDepositDetail advanceDepositDetail = advanceDeposits.remove(index);
167        this.totalAdvanceDepositAmount = this.totalAdvanceDepositAmount.subtract(advanceDepositDetail.getFinancialDocumentAdvanceDepositAmount());
168    }
169
170    /**
171     * @return Integer
172     */
173    public Integer getNextAdvanceDepositLineNumber() {
174        return nextAdvanceDepositLineNumber;
175    }
176
177    /**
178     * @param nextAdvanceDepositLineNumber
179     */
180    public void setNextAdvanceDepositLineNumber(Integer nextAdvanceDepositLineNumber) {
181        this.nextAdvanceDepositLineNumber = nextAdvanceDepositLineNumber;
182    }
183
184    /**
185     * This method returns the overall total of the document - the advance deposit total.
186     *
187     * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTotalDollarAmount()
188     * @return KualiDecimal
189     */
190    @Override
191    public KualiDecimal getTotalDollarAmount() {
192        return this.totalAdvanceDepositAmount;
193    }
194
195    /**
196     * This method defers to its parent's version of handleRouteStatusChange, but then, if the document is processed, it creates
197     * ElectronicPaymentClaim records for any qualifying accountings lines in the document.
198     *
199     * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange()
200     */
201    @Override
202    public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
203        super.doRouteStatusChange(statusChangeEvent);
204        if (getDocumentHeader().getWorkflowDocument().isProcessed()) {
205            SpringContext.getBean(ElectronicPaymentClaimingService.class).generateElectronicPaymentClaimRecords(this);
206        }
207        this.getCapitalAssetManagementModuleService().deleteDocumentAssetLocks(this);
208    }
209
210
211    /**
212     * Overrides super to call super and then also add in the new list of advance deposits that have to be managed.
213     *
214     * @see org.kuali.rice.krad.document.TransactionalDocumentBase#buildListOfDeletionAwareLists()
215     */
216    @Override
217    public List buildListOfDeletionAwareLists() {
218        List managedLists = super.buildListOfDeletionAwareLists();
219        managedLists.add(getAdvanceDeposits());
220
221        return managedLists;
222    }
223
224    /**
225     * Generates bank offset GLPEs for deposits, if enabled.
226     *
227     * @param financialDocument submitted financial document
228     * @param sequenceHelper helper class which will allows us to increment a reference without using an Integer
229     * @return true if there are no issues creating GLPE's
230     * @see org.kuali.rice.krad.rule.GenerateGeneralLedgerDocumentPendingEntriesRule#processGenerateDocumentGeneralLedgerPendingEntries(org.kuali.rice.krad.document.FinancialDocument,org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
231     */
232    @Override
233    public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
234        boolean success = true;
235
236        GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
237
238        if (SpringContext.getBean(BankService.class).isBankSpecificationEnabled()) {
239            int displayedDepositNumber = 1;
240            for (AdvanceDepositDetail detail : getAdvanceDeposits()) {
241                detail.refreshReferenceObject(KFSPropertyConstants.BANK);
242
243                GeneralLedgerPendingEntry bankOffsetEntry = new GeneralLedgerPendingEntry();
244                if (!glpeService.populateBankOffsetGeneralLedgerPendingEntry(detail.getBank(), detail.getFinancialDocumentAdvanceDepositAmount(), this, getPostingYear(), sequenceHelper, bankOffsetEntry, KFSConstants.ADVANCE_DEPOSITS_LINE_ERRORS)) {
245                    success = false;
246                    continue; // An unsuccessfully populated bank offset entry may contain invalid relations, so don't add it
247                }
248
249                AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
250                bankOffsetEntry.setTransactionLedgerEntryDescription(accountingDocumentRuleUtil.formatProperty(KFSKeyConstants.AdvanceDeposit.DESCRIPTION_GLPE_BANK_OFFSET, displayedDepositNumber++));
251                addPendingEntry(bankOffsetEntry);
252                sequenceHelper.increment();
253
254                GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(bankOffsetEntry);
255                success &= glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), bankOffsetEntry, sequenceHelper, offsetEntry);
256                addPendingEntry(offsetEntry);
257                sequenceHelper.increment();
258            }
259        }
260
261        return success;
262    }
263
264    @Override
265    public void postProcessSave(KualiDocumentEvent event) {
266        super.postProcessSave(event);
267        if (!(event instanceof SaveDocumentEvent)) { // don't lock until they route
268            String documentTypeName = SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass());
269            this.getCapitalAssetManagementModuleService().generateCapitalAssetLock(this,documentTypeName);
270        }
271    }
272
273    /**
274     * @see org.kuali.kfs.sys.document.AccountingDocumentBase#toErrorCorrection()
275     */
276    @Override
277    public void toErrorCorrection() throws WorkflowException {
278        super.toErrorCorrection();
279        correctAdvanceDepositDetails();
280        correctCapitalAccountingLines();
281    }
282
283    /**
284     * Upon error correction, negates amount in each advance deposit detail, and updates the documentNumber to point to the new document.
285     */
286    protected void correctAdvanceDepositDetails() {
287        for (AdvanceDepositDetail deposit: advanceDeposits) {
288            deposit.setVersionNumber(new Long(1));
289            deposit.setDocumentNumber(documentNumber);
290            deposit.setFinancialDocumentAdvanceDepositAmount(deposit.getFinancialDocumentAdvanceDepositAmount().negated());
291        }
292    }
293
294}