View Javadoc
1   /*
2    * Copyright 2006 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.fp.document.service.impl;
17  
18  import static org.kuali.ole.sys.document.validation.impl.AccountingDocumentRuleBaseConstants.ERROR_PATH.DOCUMENT_ERROR_PREFIX;
19  
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.ole.fp.businessobject.CashDrawer;
29  import org.kuali.ole.fp.businessobject.CashieringTransaction;
30  import org.kuali.ole.fp.businessobject.CoinDetail;
31  import org.kuali.ole.fp.businessobject.CurrencyDetail;
32  import org.kuali.ole.fp.document.CashReceiptDocument;
33  import org.kuali.ole.fp.document.CashReceiptFamilyBase;
34  import org.kuali.ole.fp.document.dataaccess.CashManagementDao;
35  import org.kuali.ole.fp.document.service.CashReceiptService;
36  import org.kuali.ole.fp.service.CashDrawerService;
37  import org.kuali.ole.sys.OLEConstants;
38  import org.kuali.ole.sys.OLEKeyConstants;
39  import org.kuali.ole.sys.OLEKeyConstants.CashReceipt;
40  import org.kuali.ole.sys.OLEPropertyConstants;
41  import org.kuali.ole.sys.context.SpringContext;
42  import org.kuali.rice.core.api.util.type.KualiDecimal;
43  import org.kuali.rice.kew.api.WorkflowDocument;
44  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
45  import org.kuali.rice.kim.api.identity.Person;
46  import org.kuali.rice.kns.service.DataDictionaryService;
47  import org.kuali.rice.kns.service.DictionaryValidationService;
48  import org.kuali.rice.krad.bo.DocumentHeader;
49  import org.kuali.rice.krad.service.BusinessObjectService;
50  import org.kuali.rice.krad.util.GlobalVariables;
51  import org.kuali.rice.krad.util.ObjectUtils;
52  import org.kuali.rice.location.api.campus.CampusService;
53  import org.springframework.transaction.annotation.Transactional;
54  
55  /**
56   * 
57   * This is the default implementation of the CashReceiptService interface.
58   */
59  @Transactional
60  public class CashReceiptServiceImpl implements CashReceiptService {
61      org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CashReceiptServiceImpl.class);
62  
63      protected BusinessObjectService businessObjectService;
64      protected CashManagementDao cashManagementDao;
65      protected CashDrawerService cashDrawerService;
66      protected DataDictionaryService dataDictionaryService;
67      protected DictionaryValidationService dictionaryValidationService;
68  
69      /**
70       * This method verifies the campus code provided exists.  This is done by retrieving all the available campuses from
71       * the BusinessObjectService and then looking for a matching campus code within the result set.
72       * 
73       * @param campusCode The campus code to be verified.
74       * @return True if the campus code provided is valid and exists, false otherwise.
75       */
76      protected boolean verifyCampus(String campusCode) {
77          return SpringContext.getBean(CampusService.class).getCampus(campusCode) != null;
78      }
79  
80  
81      /**
82       * This method retrieves the cash receipt verification unit based on the user provided.  This is done by retrieving the campus
83       * code associated with the user provided and then performing the lookup using this campus code.
84       * 
85       * @param user The user to be used to retrieve the verification unit.
86       * @return The cash receipt verification unit associated with the user provided.
87       * 
88       * @see org.kuali.ole.fp.document.service.CashReceiptService#getCashReceiptVerificationUnit(org.kuali.rice.krad.bo.user.KualiUser)
89       */
90      @Override
91      public String getCashReceiptVerificationUnitForUser(Person user) {
92          String unitName = null;
93  
94          if (user == null) {
95              throw new IllegalArgumentException("invalid (null) user");
96          }
97  
98          return user.getCampusCode();
99      }
100 
101 
102     /**
103      * This method retrieves a collection of cash receipts using the verification unit and the status provided to 
104      * retrieve the cash receipts.  
105      * 
106      * @param verificationUnit The verification unit used to retrieve a collection of associated cash receipts.
107      * @param statusCode The status code of the cash receipts to be retrieved.  
108      * @return A collection of cash receipt documents which match the search criteria provided.
109      * 
110      * @see org.kuali.ole.fp.document.service.CashReceiptService#getCashReceipts(java.lang.String, java.lang.String)
111      */
112     @Override
113     public List getCashReceipts(String verificationUnit, String statusCode) {
114         if (StringUtils.isBlank(statusCode)) {
115             throw new IllegalArgumentException("invalid (blank) statusCode");
116         }
117 
118         String[] statii = new String[] { statusCode };
119         return getCashReceipts(verificationUnit, statii);
120     }
121 
122     /**
123      * This method retrieves a collection of cash receipts using the verification unit and the statuses provided to 
124      * retrieve the cash receipts.  
125      * 
126      * @param verificationUnit The verification unit used to retrieve a collection of associated cash receipts.
127      * @param statii A collection of possible statuses that will be used in the lookup of cash receipts.
128      * @return A collection of cash receipt documents which match the search criteria provided.
129      * 
130      * @see org.kuali.ole.fp.document.service.CashReceiptService#getCashReceipts(java.lang.String, java.lang.String[])
131      */
132     @Override
133     public List getCashReceipts(String verificationUnit, String[] statii) {
134         if (StringUtils.isBlank(verificationUnit)) {
135             throw new IllegalArgumentException("invalid (blank) verificationUnit");
136         }
137         if (statii == null) {
138             throw new IllegalArgumentException("invalid (null) statii");
139         }
140         else {
141             if (statii.length == 0) {
142                 throw new IllegalArgumentException("invalid (empty) statii");
143             }
144             else {
145                 for (int i = 0; i < statii.length; ++i) {
146                     if (StringUtils.isBlank(statii[i])) {
147                         throw new IllegalArgumentException("invalid (blank) status code " + i);
148                     }
149                 }
150             }
151         }
152 
153         return getPopulatedCashReceipts(verificationUnit, statii);
154     }
155 
156     /**
157      * This method retrieves a populated collection of cash receipts using the lookup parameters provided.  A populated 
158      * cash receipt document is a cash receipt document with fully populated workflow fields.
159      * 
160      * @param verificationUnit The verification unit used to retrieve a collection of associated cash receipts.
161      * @param statii A collection of possible statuses that will be used in the lookup of the cash receipts.
162      * @return List of CashReceiptDocument instances with their associated workflowDocuments populated.
163      */
164     public List<CashReceiptDocument> getPopulatedCashReceipts(String verificationUnit, String[] statii) {
165         Map queryCriteria = buildCashReceiptCriteriaMap(verificationUnit, statii);
166 
167         List<CashReceiptDocument> documents = new ArrayList<CashReceiptDocument>(businessObjectService.findMatchingOrderBy(CashReceiptDocument.class, queryCriteria, OLEPropertyConstants.DOCUMENT_NUMBER, true));
168 
169         populateWorkflowFields(documents);
170 
171         return documents;
172     }
173 
174 
175     /**
176      * This method builds out a map of search criteria for performing cash receipt lookups using the values provided.
177      * 
178      * @param campusCode The campus code to use as search criteria for looking up cash receipts.
179      * @param statii A collection of possible statuses to use as search criteria for looking up cash receipts.
180      * @return The search criteria provided in a map with CashReceiptConstants used as keys to the parameters given.
181      */
182     protected Map buildCashReceiptCriteriaMap(String campusCode, String[] statii) {
183         Map queryCriteria = new HashMap();
184 
185         if (statii.length == 1) {
186             queryCriteria.put(OLEConstants.CashReceiptConstants.CASH_RECEIPT_DOC_HEADER_STATUS_CODE_PROPERTY_NAME, statii[0]);
187         }
188         else if (statii.length > 0) {
189             List<String> statusList = Arrays.asList(statii);
190             queryCriteria.put(OLEConstants.CashReceiptConstants.CASH_RECEIPT_DOC_HEADER_STATUS_CODE_PROPERTY_NAME, statusList);
191         }
192 
193         queryCriteria.put(OLEConstants.CashReceiptConstants.CASH_RECEIPT_CAMPUS_LOCATION_CODE_PROPERTY_NAME, campusCode);
194 
195         return queryCriteria;
196     }
197 
198     /**
199      * This method populates the workflowDocument field of each CashReceiptDocument in the given List
200      * 
201      * @param documents A collection of CashReceiptDocuments to be populated with workflow document data.
202      */
203     protected void populateWorkflowFields(List documents) {
204         for (Iterator i = documents.iterator(); i.hasNext();) {
205             CashReceiptDocument cr = (CashReceiptDocument) i.next();
206             DocumentHeader docHeader = cr.getDocumentHeader();
207             WorkflowDocument workflowDocument = WorkflowDocumentFactory.loadDocument(GlobalVariables.getUserSession().getPrincipalId(), docHeader.getDocumentNumber());
208     
209             docHeader.setWorkflowDocument(workflowDocument);
210         }
211     }
212 
213     /**
214      * This method retrieves the cash details from the cash receipt document provided and adds those details to the 
215      * associated cash drawer.  After the details are added to the drawer, the drawer is persisted to the database.
216      * 
217      * @param crDoc The cash receipt document the cash details will be retrieved from.
218      * 
219      * @see org.kuali.ole.fp.document.service.CashReceiptService#addCashDetailsToCashDrawer(org.kuali.ole.fp.document.CashReceiptDocument)
220      */
221     @Override
222     public void addCashDetailsToCashDrawer(CashReceiptDocument crDoc) {
223         CashDrawer drawer = retrieveCashDrawer(crDoc);
224         // we need to to add the currency and coin to the cash management doc's cumulative CR as well
225         if (crDoc.getCurrencyDetail() != null && !crDoc.getCurrencyDetail().isEmpty()) {
226             CurrencyDetail cumulativeCurrencyDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(drawer.getReferenceFinancialDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, OLEConstants.CurrencyCoinSources.CASH_RECEIPTS);
227             cumulativeCurrencyDetail.add(crDoc.getConfirmedCurrencyDetail());
228             cumulativeCurrencyDetail.subtract(crDoc.getChangeCurrencyDetail());
229             businessObjectService.save(cumulativeCurrencyDetail);
230             
231             drawer.addCurrency(crDoc.getConfirmedCurrencyDetail());
232             drawer.removeCurrency(crDoc.getChangeCurrencyDetail());
233         }
234         if (crDoc.getCoinDetail() != null && !crDoc.getCoinDetail().isEmpty()) {
235             CoinDetail cumulativeCoinDetail = cashManagementDao.findCoinDetailByCashieringStatus(drawer.getReferenceFinancialDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, OLEConstants.CurrencyCoinSources.CASH_RECEIPTS);
236             cumulativeCoinDetail.add(crDoc.getConfirmedCoinDetail());
237             cumulativeCoinDetail.subtract(crDoc.getChangeCoinDetail());
238             businessObjectService.save(cumulativeCoinDetail);
239             
240             drawer.addCoin(crDoc.getConfirmedCoinDetail());
241             drawer.removeCoin(crDoc.getChangeCoinDetail());
242         }
243         businessObjectService.save(drawer);
244     }
245     
246     /**
247      * This method finds the appropriate cash drawer for this cash receipt document to add cash to.
248      * 
249      * @param crDoc The document the cash drawer will be retrieved from.
250      * @return An instance of a cash drawer associated with the cash receipt document provided.
251      */
252     protected CashDrawer retrieveCashDrawer(CashReceiptDocument crDoc) {
253         String campusCode = crDoc.getCampusLocationCode();
254         if (campusCode == null) {
255             throw new RuntimeException("Cannot find workgroup name for Cash Receipt document: "+crDoc.getDocumentNumber());
256         }
257         
258         CashDrawer drawer = cashDrawerService.getByCampusCode(campusCode);
259         if (drawer == null) {
260             throw new RuntimeException("There is no Cash Drawer for Workgroup "+campusCode);
261         }
262         return drawer;
263     }
264     
265     /**
266      * @see org.kuali.module.financial.service.CashReceiptTotalsVerificationService#areCashTotalsInvalid(org.kuali.ole.fp.document.CashReceiptDocument)
267      */
268     @Override
269     public boolean areCashTotalsInvalid(CashReceiptDocument cashReceiptDocument) {
270         String documentEntryName = cashReceiptDocument.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
271 
272         boolean isInvalid = isTotalInvalid(cashReceiptDocument, cashReceiptDocument.getTotalCheckAmount(), documentEntryName, OLEPropertyConstants.TOTAL_CHECK_AMOUNT);
273         isInvalid |= isTotalInvalid(cashReceiptDocument, cashReceiptDocument.getTotalCashAmount(), documentEntryName, OLEPropertyConstants.TOTAL_CASH_AMOUNT);
274         isInvalid |= isTotalInvalid(cashReceiptDocument, cashReceiptDocument.getTotalCoinAmount(), documentEntryName, OLEPropertyConstants.TOTAL_COIN_AMOUNT);
275 
276         isInvalid |= isTotalInvalid(cashReceiptDocument, cashReceiptDocument.getTotalDollarAmount(), documentEntryName, OLEPropertyConstants.SUM_TOTAL_AMOUNT);
277 
278         return isInvalid;
279     }
280 
281     /**
282      * Puts an error message in the error map for that property if the amount is negative.
283      * 
284      * @param cashReceiptDocument submitted cash receipt document
285      * @param totalAmount total amount (cash total, check total, etc)
286      * @param documentEntryName document type
287      * @param propertyName property type (i.e totalCashAmount, totalCheckAmount, etc)
288      * @return true if the totalAmount is an invalid value
289      */
290     protected boolean isTotalInvalid(CashReceiptFamilyBase cashReceiptDocument, KualiDecimal totalAmount, String documentEntryName, String propertyName) {
291         boolean isInvalid = false;
292         String errorProperty = DOCUMENT_ERROR_PREFIX + propertyName;
293 
294         if (totalAmount != null) {
295             if (totalAmount.isNegative()) {
296                 String errorLabel = dataDictionaryService.getAttributeLabel(documentEntryName, propertyName);
297                 GlobalVariables.getMessageMap().putError(errorProperty, CashReceipt.ERROR_NEGATIVE_TOTAL, errorLabel);
298 
299                 isInvalid = true;
300             } else {
301                 int precount = GlobalVariables.getMessageMap().getNumberOfPropertiesWithErrors();
302 
303                 dictionaryValidationService.validateDocumentAttribute(cashReceiptDocument, propertyName, DOCUMENT_ERROR_PREFIX);
304 
305                 String errorLabel = dataDictionaryService.getAttributeLabel(documentEntryName, propertyName);
306                 // replace generic error message, if any, with something more readable
307                 GlobalVariables.getMessageMap().replaceError(errorProperty, OLEKeyConstants.ERROR_MAX_LENGTH, CashReceipt.ERROR_EXCESSIVE_TOTAL, errorLabel);
308 
309                 int postcount = GlobalVariables.getMessageMap().getNumberOfPropertiesWithErrors();
310                 isInvalid = (postcount > precount);
311             }
312         }
313 
314         return isInvalid;
315     }
316 
317     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
318         this.businessObjectService = businessObjectService;
319     }
320     public void setCashManagementDao(CashManagementDao cashManagementDao) {
321         this.cashManagementDao = cashManagementDao;
322     }
323     public void setCashDrawerService(CashDrawerService cashDrawerService) {
324         this.cashDrawerService = cashDrawerService;
325     }
326     public void setDictionaryValidationService(DictionaryValidationService dictionaryValidationService) {
327         this.dictionaryValidationService = dictionaryValidationService;
328     }
329     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
330         this.dataDictionaryService = dataDictionaryService;
331     }
332 
333 }
334