001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.purap.service.impl;
017:
018: import java.math.BigDecimal;
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024:
025: import org.kuali.core.bo.DocumentHeader;
026: import org.kuali.core.service.DataDictionaryService;
027: import org.kuali.core.util.GlobalVariables;
028: import org.kuali.core.util.KualiDecimal;
029: import org.kuali.kfs.KFSPropertyConstants;
030: import org.kuali.kfs.context.SpringContext;
031: import org.kuali.module.purap.PurapConstants;
032: import org.kuali.module.purap.PurapKeyConstants;
033: import org.kuali.module.purap.bo.CreditMemoItem;
034: import org.kuali.module.purap.bo.PaymentRequestAccount;
035: import org.kuali.module.purap.bo.PaymentRequestItem;
036: import org.kuali.module.purap.bo.PurchaseOrderItem;
037: import org.kuali.module.purap.document.CreditMemoDocument;
038: import org.kuali.module.purap.document.PaymentRequestDocument;
039: import org.kuali.module.purap.document.PurchaseOrderDocument;
040: import org.kuali.module.purap.service.AccountsPayableService;
041: import org.kuali.module.purap.service.CreditMemoCreateService;
042: import org.kuali.module.purap.service.CreditMemoService;
043: import org.kuali.module.purap.service.PaymentRequestService;
044: import org.kuali.module.purap.service.PurapService;
045: import org.kuali.module.purap.service.PurchaseOrderService;
046: import org.kuali.module.purap.util.ExpiredOrClosedAccountEntry;
047: import org.kuali.module.vendor.VendorConstants;
048: import org.kuali.module.vendor.bo.VendorAddress;
049: import org.kuali.module.vendor.bo.VendorDetail;
050: import org.kuali.module.vendor.service.VendorService;
051: import org.kuali.module.vendor.util.VendorUtils;
052:
053: /**
054: * Performs initial population of the credit memo document.
055: */
056: public class CreditMemoCreateServiceImpl implements
057: CreditMemoCreateService {
058: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
059: .getLogger(CreditMemoServiceImpl.class);
060: private VendorService vendorService;
061: private CreditMemoService creditMemoService;
062:
063: /**
064: * @see org.kuali.module.purap.service.CreditMemoCreateService#populateDocumentAfterInit(org.kuali.module.purap.document.CreditMemoDocument)
065: */
066: public void populateDocumentAfterInit(CreditMemoDocument cmDocument) {
067:
068: // make a call to search for expired/closed accounts
069: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList = SpringContext
070: .getBean(AccountsPayableService.class)
071: .getExpiredOrClosedAccountList(cmDocument);
072:
073: if (cmDocument.isSourceDocumentPaymentRequest()) {
074: populateDocumentFromPreq(cmDocument,
075: expiredOrClosedAccountList);
076: } else if (cmDocument.isSourceDocumentPurchaseOrder()) {
077: populateDocumentFromPO(cmDocument,
078: expiredOrClosedAccountList);
079: } else {
080: populateDocumentFromVendor(cmDocument);
081: }
082:
083: populateDocumentDescription(cmDocument);
084:
085: // write a note for expired/closed accounts if any exist and add a message stating there were expired/closed accounts at the
086: // top of the document
087: SpringContext.getBean(AccountsPayableService.class)
088: .generateExpiredOrClosedAccountNote(cmDocument,
089: expiredOrClosedAccountList);
090:
091: // set indicator so a message is displayed for accounts that were replaced due to expired/closed status
092: if (!expiredOrClosedAccountList.isEmpty()) {
093: cmDocument.setContinuationAccountIndicator(true);
094: }
095:
096: }
097:
098: /**
099: * Populate Credit Memo of type Payment Request.
100: *
101: * @param cmDocument - Credit Memo Document to Populate
102: */
103: protected void populateDocumentFromPreq(
104: CreditMemoDocument cmDocument,
105: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
106: PaymentRequestDocument paymentRequestDocument = SpringContext
107: .getBean(PaymentRequestService.class)
108: .getPaymentRequestById(
109: cmDocument.getPaymentRequestIdentifier());
110: cmDocument.getDocumentHeader().setOrganizationDocumentNumber(
111: paymentRequestDocument.getDocumentHeader()
112: .getOrganizationDocumentNumber());
113: cmDocument.setPaymentRequestDocument(paymentRequestDocument);
114: cmDocument.setPurchaseOrderDocument(paymentRequestDocument
115: .getPurchaseOrderDocument());
116:
117: // credit memo address taken directly from payment request
118: cmDocument
119: .setVendorHeaderGeneratedIdentifier(paymentRequestDocument
120: .getVendorHeaderGeneratedIdentifier());
121: cmDocument
122: .setVendorDetailAssignedIdentifier(paymentRequestDocument
123: .getVendorDetailAssignedIdentifier());
124: cmDocument
125: .setVendorAddressGeneratedIdentifier(paymentRequestDocument
126: .getVendorAddressGeneratedIdentifier());
127: cmDocument.setVendorCustomerNumber(paymentRequestDocument
128: .getVendorCustomerNumber());
129: cmDocument
130: .setVendorName(paymentRequestDocument.getVendorName());
131: cmDocument.setVendorLine1Address(paymentRequestDocument
132: .getVendorLine1Address());
133: cmDocument.setVendorLine2Address(paymentRequestDocument
134: .getVendorLine2Address());
135: cmDocument.setVendorCityName(paymentRequestDocument
136: .getVendorCityName());
137: cmDocument.setVendorStateCode(paymentRequestDocument
138: .getVendorStateCode());
139: cmDocument.setVendorPostalCode(paymentRequestDocument
140: .getVendorPostalCode());
141: cmDocument.setVendorCountryCode(paymentRequestDocument
142: .getVendorCountryCode());
143: cmDocument
144: .setAccountsPayablePurchasingDocumentLinkIdentifier(paymentRequestDocument
145: .getAccountsPayablePurchasingDocumentLinkIdentifier());
146:
147: // prep the item lines (also collect warnings for later display) this is only done on paymentRequest
148: convertMoneyToPercent(paymentRequestDocument);
149: populateItemLinesFromPreq(cmDocument,
150: expiredOrClosedAccountList);
151: }
152:
153: /**
154: * Populates the credit memo items from the payment request items.
155: *
156: * @param cmDocument - Credit Memo Document to Populate
157: */
158: protected void populateItemLinesFromPreq(
159: CreditMemoDocument cmDocument,
160: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
161: PaymentRequestDocument preqDocument = cmDocument
162: .getPaymentRequestDocument();
163:
164: for (PaymentRequestItem preqItemToTemplate : (List<PaymentRequestItem>) preqDocument
165: .getItems()) {
166: if (preqItemToTemplate.getItemType()
167: .isItemTypeAboveTheLineIndicator()) {
168: cmDocument.getItems().add(
169: new CreditMemoItem(cmDocument,
170: preqItemToTemplate, preqItemToTemplate
171: .getPurchaseOrderItem(),
172: expiredOrClosedAccountList));
173: }
174: }
175:
176: // add below the line items
177: SpringContext.getBean(PurapService.class).addBelowLineItems(
178: cmDocument);
179: }
180:
181: /**
182: * Populate Credit Memo of type Purchase Order.
183: *
184: * @param cmDocument - Credit Memo Document to Populate
185: */
186: protected void populateDocumentFromPO(
187: CreditMemoDocument cmDocument,
188: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
189: PurchaseOrderDocument purchaseOrderDocument = (SpringContext
190: .getBean(PurchaseOrderService.class)
191: .getCurrentPurchaseOrder(cmDocument
192: .getPurchaseOrderIdentifier()));
193: cmDocument.setPurchaseOrderDocument(purchaseOrderDocument);
194: cmDocument.getDocumentHeader().setOrganizationDocumentNumber(
195: purchaseOrderDocument.getDocumentHeader()
196: .getOrganizationDocumentNumber());
197:
198: cmDocument
199: .setVendorHeaderGeneratedIdentifier(purchaseOrderDocument
200: .getVendorHeaderGeneratedIdentifier());
201: cmDocument
202: .setVendorDetailAssignedIdentifier(purchaseOrderDocument
203: .getVendorDetailAssignedIdentifier());
204: cmDocument.setVendorCustomerNumber(purchaseOrderDocument
205: .getVendorCustomerNumber());
206: cmDocument.setVendorName(purchaseOrderDocument.getVendorName());
207: cmDocument
208: .setAccountsPayablePurchasingDocumentLinkIdentifier(purchaseOrderDocument
209: .getAccountsPayablePurchasingDocumentLinkIdentifier());
210:
211: // populate cm vendor address with the default remit address type for the vendor if found
212: String userCampus = GlobalVariables.getUserSession()
213: .getUniversalUser().getCampusCode();
214: VendorAddress vendorAddress = vendorService
215: .getVendorDefaultAddress(purchaseOrderDocument
216: .getVendorHeaderGeneratedIdentifier(),
217: purchaseOrderDocument
218: .getVendorDetailAssignedIdentifier(),
219: VendorConstants.AddressTypes.REMIT, userCampus);
220: if (vendorAddress != null) {
221: cmDocument.templateVendorAddress(vendorAddress);
222: cmDocument
223: .setVendorAddressGeneratedIdentifier(vendorAddress
224: .getVendorAddressGeneratedIdentifier());
225: } else {
226: // set address from PO
227: cmDocument
228: .setVendorAddressGeneratedIdentifier(purchaseOrderDocument
229: .getVendorAddressGeneratedIdentifier());
230: cmDocument.setVendorLine1Address(purchaseOrderDocument
231: .getVendorLine1Address());
232: cmDocument.setVendorLine2Address(purchaseOrderDocument
233: .getVendorLine2Address());
234: cmDocument.setVendorCityName(purchaseOrderDocument
235: .getVendorCityName());
236: cmDocument.setVendorStateCode(purchaseOrderDocument
237: .getVendorStateCode());
238: cmDocument.setVendorPostalCode(purchaseOrderDocument
239: .getVendorPostalCode());
240: cmDocument.setVendorCountryCode(purchaseOrderDocument
241: .getVendorCountryCode());
242: }
243:
244: populateItemLinesFromPO(cmDocument, expiredOrClosedAccountList);
245: }
246:
247: /**
248: * Populates the credit memo items from the payment request items.
249: *
250: * @param cmDocument - Credit Memo Document to Populate
251: */
252: protected void populateItemLinesFromPO(
253: CreditMemoDocument cmDocument,
254: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
255: List<PurchaseOrderItem> invoicedItems = creditMemoService
256: .getPOInvoicedItems(cmDocument
257: .getPurchaseOrderDocument());
258: for (PurchaseOrderItem poItem : invoicedItems) {
259: cmDocument.getItems().add(
260: new CreditMemoItem(cmDocument, poItem,
261: expiredOrClosedAccountList));
262: }
263:
264: // add below the line items
265: SpringContext.getBean(PurapService.class).addBelowLineItems(
266: cmDocument);
267: }
268:
269: /**
270: * Populate Credit Memo of type Vendor.
271: *
272: * @param cmDocument - Credit Memo Document to Populate
273: */
274: protected void populateDocumentFromVendor(
275: CreditMemoDocument cmDocument) {
276: Integer vendorHeaderId = VendorUtils
277: .getVendorHeaderId(cmDocument.getVendorNumber());
278: Integer vendorDetailId = VendorUtils
279: .getVendorDetailId(cmDocument.getVendorNumber());
280:
281: VendorDetail vendorDetail = SpringContext.getBean(
282: VendorService.class).getVendorDetail(vendorHeaderId,
283: vendorDetailId);
284: cmDocument.setVendorDetail(vendorDetail);
285:
286: cmDocument.setVendorHeaderGeneratedIdentifier(vendorDetail
287: .getVendorHeaderGeneratedIdentifier());
288: cmDocument.setVendorDetailAssignedIdentifier(vendorDetail
289: .getVendorDetailAssignedIdentifier());
290: cmDocument.setVendorCustomerNumber(vendorDetail
291: .getVendorNumber());
292: cmDocument.setVendorName(vendorDetail.getVendorName());
293:
294: // credit memo type vendor uses the default remit type address for the vendor if found
295: String userCampus = GlobalVariables.getUserSession()
296: .getUniversalUser().getCampusCode();
297: VendorAddress vendorAddress = vendorService
298: .getVendorDefaultAddress(vendorHeaderId,
299: vendorDetailId,
300: VendorConstants.AddressTypes.REMIT, userCampus);
301: if (vendorAddress == null) {
302: // pick up the default vendor po address type
303: vendorAddress = vendorService.getVendorDefaultAddress(
304: vendorHeaderId, vendorDetailId,
305: VendorConstants.AddressTypes.PURCHASE_ORDER,
306: userCampus);
307: }
308:
309: cmDocument.setVendorAddressGeneratedIdentifier(vendorAddress
310: .getVendorAddressGeneratedIdentifier());
311: cmDocument.templateVendorAddress(vendorAddress);
312:
313: // add below the line items
314: SpringContext.getBean(PurapService.class).addBelowLineItems(
315: cmDocument);
316: }
317:
318: /**
319: * Converts the amount to percent and updates the percent field on the CreditMemoAccount
320: *
321: * @param pr The payment request document containing the accounts whose percentage would be set.
322: */
323: private void convertMoneyToPercent(PaymentRequestDocument pr) {
324: LOG.debug("convertMoneyToPercent() started");
325: Collection errors = new ArrayList();
326: int itemNbr = 0;
327:
328: for (Iterator iter = pr.getItems().iterator(); iter.hasNext();) {
329: PaymentRequestItem item = (PaymentRequestItem) iter.next();
330:
331: itemNbr++;
332: String identifier = item.getItemIdentifierString();
333:
334: if (item.getExtendedPrice().isNonZero()) {
335:
336: KualiDecimal accountTotal = KualiDecimal.ZERO;
337: int accountIdentifier = 0;
338: for (Iterator iterator = item
339: .getSourceAccountingLines().iterator(); iterator
340: .hasNext();) {
341: accountIdentifier++;
342: PaymentRequestAccount account = (PaymentRequestAccount) iterator
343: .next();
344: KualiDecimal accountAmount = account.getAmount();
345: BigDecimal tmpPercent = BigDecimal.ZERO;
346: KualiDecimal extendedPrice = item
347: .getExtendedPrice();
348: tmpPercent = accountAmount.bigDecimalValue()
349: .divide(
350: extendedPrice.bigDecimalValue(),
351: PurapConstants.PRORATION_SCALE
352: .intValue(),
353: KualiDecimal.ROUND_BEHAVIOR);
354: // test that the above amount is correct, if so just check that the total of all these matches the item total
355:
356: KualiDecimal calcAmount = new KualiDecimal(
357: tmpPercent.multiply(extendedPrice
358: .bigDecimalValue()));
359: if (calcAmount.compareTo(accountAmount) != 0) {
360: // rounding error
361: LOG
362: .debug("convertMoneyToPercent() Rounding error on "
363: + account);
364: String param1 = identifier + "."
365: + accountIdentifier;
366: String param2 = calcAmount
367: .bigDecimalValue()
368: .subtract(
369: accountAmount.bigDecimalValue())
370: .toString();
371: GlobalVariables
372: .getErrorMap()
373: .putError(
374: item.getItemIdentifierString(),
375: PurapKeyConstants.ERROR_ITEM_ACCOUNTING_ROUNDING,
376: param1, param2);
377: account.setAmount(calcAmount);
378: }
379:
380: // update percent
381: LOG
382: .debug("convertMoneyToPercent() updating percent to "
383: + tmpPercent);
384: account.setAccountLinePercent(tmpPercent
385: .multiply(new BigDecimal(100)));
386:
387: // check total based on adjusted amount
388: accountTotal = accountTotal.add(calcAmount);
389:
390: }
391: if (!accountTotal.equals(item.getExtendedPrice())) {
392: GlobalVariables
393: .getErrorMap()
394: .putError(
395: item.getItemIdentifierString(),
396: PurapKeyConstants.ERROR_ITEM_ACCOUNTING_DOLLAR_TOTAL,
397: identifier,
398: accountTotal.toString(),
399: item.getExtendedPrice().toString());
400: LOG.debug("Invalid Totals");
401: }
402: }
403: }
404: }
405:
406: /**
407: * Defaults the document description based on the credit memo source type.
408: *
409: * @param cmDocument - Credit Memo Document to Populate
410: */
411: private void populateDocumentDescription(
412: CreditMemoDocument cmDocument) {
413: String description = "";
414: if (cmDocument.isSourceVendor()) {
415: description = "Vendor: " + cmDocument.getVendorName();
416: } else {
417: description = "PO: "
418: + cmDocument.getPurchaseOrderDocument()
419: .getPurapDocumentIdentifier() + " Vendor: "
420: + cmDocument.getVendorName();
421: }
422:
423: // trim description if longer than whats specified in the data dictionary
424: int noteTextMaxLength = SpringContext.getBean(
425: DataDictionaryService.class).getAttributeMaxLength(
426: DocumentHeader.class,
427: KFSPropertyConstants.FINANCIAL_DOCUMENT_DESCRIPTION)
428: .intValue();
429: if (noteTextMaxLength < description.length()) {
430: description = description.substring(0, noteTextMaxLength);
431: }
432:
433: cmDocument.getDocumentHeader().setFinancialDocumentDescription(
434: description);
435: }
436:
437: public void setCreditMemoService(CreditMemoService creditMemoService) {
438: this .creditMemoService = creditMemoService;
439: }
440:
441: public void setVendorService(VendorService vendorService) {
442: this.vendorService = vendorService;
443: }
444: }
|