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.module.ar.document; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.sql.Timestamp; 021 import java.util.ArrayList; 022 import java.util.Iterator; 023 import java.util.List; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.kuali.kfs.module.ar.ArConstants; 027 import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader; 028 import org.kuali.kfs.module.ar.businessobject.CustomerCreditMemoDetail; 029 import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail; 030 import org.kuali.kfs.module.ar.businessobject.ReceivableCustomerCreditMemoDetail; 031 import org.kuali.kfs.module.ar.businessobject.ReceivableCustomerInvoiceDetail; 032 import org.kuali.kfs.module.ar.businessobject.SalesTaxCustomerCreditMemoDetail; 033 import org.kuali.kfs.module.ar.document.service.AccountsReceivableDocumentHeaderService; 034 import org.kuali.kfs.module.ar.document.service.AccountsReceivableTaxService; 035 import org.kuali.kfs.module.ar.document.service.CustomerCreditMemoDocumentService; 036 import org.kuali.kfs.module.ar.document.service.CustomerInvoiceDetailService; 037 import org.kuali.kfs.module.ar.document.service.CustomerInvoiceGLPEService; 038 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; 039 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper; 040 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail; 041 import org.kuali.kfs.sys.businessobject.TaxDetail; 042 import org.kuali.kfs.sys.context.SpringContext; 043 import org.kuali.kfs.sys.document.AmountTotaling; 044 import org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource; 045 import org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase; 046 import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService; 047 import org.kuali.kfs.sys.service.TaxService; 048 import org.kuali.kfs.sys.service.UniversityDateService; 049 import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO; 050 import org.kuali.rice.kns.exception.ValidationException; 051 import org.kuali.rice.kns.rule.event.BlanketApproveDocumentEvent; 052 import org.kuali.rice.kns.rule.event.KualiDocumentEvent; 053 import org.kuali.rice.kns.service.DataDictionaryService; 054 import org.kuali.rice.kns.service.DateTimeService; 055 import org.kuali.rice.kns.service.ParameterService; 056 import org.kuali.rice.kns.util.DateUtils; 057 import org.kuali.rice.kns.util.KualiDecimal; 058 import org.kuali.rice.kns.util.ObjectUtils; 059 import org.kuali.rice.kns.util.TypedArrayList; 060 import org.kuali.rice.kns.web.format.CurrencyFormatter; 061 062 /** 063 * @author Kuali Nervous System Team (kualidev@oncourse.iu.edu) 064 */ 065 public class CustomerCreditMemoDocument extends GeneralLedgerPostingDocumentBase implements GeneralLedgerPendingEntrySource, AmountTotaling { 066 067 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CustomerCreditMemoDocument.class); 068 069 protected String statusCode; 070 protected String financialDocumentReferenceInvoiceNumber; 071 072 protected KualiDecimal crmTotalItemAmount = KualiDecimal.ZERO; 073 protected KualiDecimal crmTotalTaxAmount = KualiDecimal.ZERO; 074 protected KualiDecimal crmTotalAmount = KualiDecimal.ZERO; 075 076 protected Integer invOutstandingDays; 077 078 protected CustomerInvoiceDocument invoice; 079 protected AccountsReceivableDocumentHeader accountsReceivableDocumentHeader; 080 081 protected List<CustomerCreditMemoDetail> creditMemoDetails; 082 083 protected transient TaxService taxService; 084 protected transient AccountsReceivableTaxService arTaxService; 085 086 public CustomerCreditMemoDocument() { 087 super(); 088 setPostingYear(SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear()); 089 creditMemoDetails = new TypedArrayList(CustomerCreditMemoDetail.class); 090 setGeneralLedgerPendingEntries(new ArrayList<GeneralLedgerPendingEntry>()); 091 } 092 093 /** 094 * Gets the creditMemoDetails attribute. 095 * @return Returns the creditMemoDetails. 096 */ 097 public List<CustomerCreditMemoDetail> getCreditMemoDetails() { 098 return creditMemoDetails; 099 } 100 101 /** 102 * Sets the creditMemoDetails attribute value. 103 * @param creditMemoDetails The creditMemoDetails to set. 104 */ 105 public void setCreditMemoDetails(List<CustomerCreditMemoDetail> creditMemoDetails) { 106 this.creditMemoDetails = creditMemoDetails; 107 } 108 109 /** 110 * Gets the financialDocumentReferenceInvoiceNumber attribute. 111 * @return Returns the financialDocumentReferenceInvoiceNumber. 112 */ 113 public String getFinancialDocumentReferenceInvoiceNumber() { 114 return financialDocumentReferenceInvoiceNumber; 115 } 116 117 /** 118 * Sets the financialDocumentReferenceInvoiceNumber attribute value. 119 * @param financialDocumentReferenceInvoiceNumber The financialDocumentReferenceInvoiceNumber to set. 120 */ 121 public void setFinancialDocumentReferenceInvoiceNumber(String financialDocumentReferenceInvoiceNumber) { 122 if (financialDocumentReferenceInvoiceNumber != null) 123 financialDocumentReferenceInvoiceNumber = financialDocumentReferenceInvoiceNumber.toUpperCase(); 124 125 this.financialDocumentReferenceInvoiceNumber = financialDocumentReferenceInvoiceNumber; 126 } 127 128 /** 129 * Gets the invoice attribute. 130 * @return Returns the invoice. 131 */ 132 public CustomerInvoiceDocument getInvoice() { 133 if (ObjectUtils.isNull(invoice) && StringUtils.isNotEmpty(financialDocumentReferenceInvoiceNumber)){ 134 refreshReferenceObject("invoice"); 135 } 136 137 return invoice; 138 } 139 140 /** 141 * Sets the invoice attribute value. 142 * @param invoice The invoice to set. 143 */ 144 public void setInvoice(CustomerInvoiceDocument invoice) { 145 this.invoice = invoice; 146 } 147 148 /** 149 * Gets the statusCode attribute. 150 * @return Returns the statusCode. 151 */ 152 public String getStatusCode() { 153 return statusCode; 154 } 155 156 /** 157 * Sets the statusCode attribute value. 158 * @param statusCode The statusCode to set. 159 */ 160 public void setStatusCode(String statusCode) { 161 this.statusCode = statusCode; 162 } 163 164 /** 165 * Initializes the values for a new document. 166 */ 167 public void initiateDocument() { 168 LOG.debug("initiateDocument() started"); 169 setStatusCode(ArConstants.CustomerCreditMemoStatuses.INITIATE); 170 } 171 172 /** 173 * Clear out the initially populated fields. 174 */ 175 public void clearInitFields() { 176 LOG.debug("clearDocument() started"); 177 178 // Clearing document Init fields 179 setFinancialDocumentReferenceInvoiceNumber(null); 180 } 181 182 /** 183 * Gets the crmTotalAmount attribute. 184 * @return Returns the crmTotalAmount. 185 */ 186 public KualiDecimal getCrmTotalAmount() { 187 return crmTotalAmount; 188 } 189 190 /** 191 * This method returns the crmTotalAmount as a currency formatted string. 192 * @return String 193 */ 194 public String getCurrencyFormattedCrmTotalAmount() { 195 return (String) new CurrencyFormatter().format(crmTotalAmount); 196 } 197 198 /** 199 * Sets the crmTotalAmount attribute value. 200 * @param crmTotalAmount The crmTotalAmount to set. 201 */ 202 public void setCrmTotalAmount(KualiDecimal crmTotalAmount) { 203 this.crmTotalAmount = crmTotalAmount; 204 } 205 206 /** 207 * Gets the crmTotalItemAmount attribute. 208 * @return Returns the crmTotalItemAmount. 209 */ 210 public KualiDecimal getCrmTotalItemAmount() { 211 return crmTotalItemAmount; 212 } 213 214 /** 215 * This method returns the crmTotalItemAmount as a currency formatted string. 216 * @return String 217 */ 218 public String getCurrencyFormattedCrmTotalItemAmount() { 219 return (String) new CurrencyFormatter().format(crmTotalItemAmount); 220 } 221 222 /** 223 * Sets the crmTotalItemAmount attribute value. 224 * @param crmTotalItemAmount The crmTotalItemAmount to set. 225 */ 226 public void setCrmTotalItemAmount(KualiDecimal crmTotalItemAmount) { 227 this.crmTotalItemAmount = crmTotalItemAmount; 228 } 229 230 /** 231 * Gets the crmTotalTaxAmount attribute. 232 * @return Returns the crmTotalTaxAmount. 233 */ 234 public KualiDecimal getCrmTotalTaxAmount() { 235 return crmTotalTaxAmount; 236 } 237 238 /** 239 * This method returns the crmTotalTaxAmount as a currency formatted string. 240 * @return String 241 */ 242 public String getCurrencyFormattedCrmTotalTaxAmount() { 243 return (String) new CurrencyFormatter().format(crmTotalTaxAmount); 244 } 245 246 /** 247 * Sets the crmTotalTaxAmount attribute value. 248 * @param crmTotalTaxAmount The crmTotalTaxAmount to set. 249 */ 250 public void setCrmTotalTaxAmount(KualiDecimal crmTotalTaxAmount) { 251 this.crmTotalTaxAmount = crmTotalTaxAmount; 252 } 253 254 /** 255 * Gets the invOutstandingDays attribute. 256 * @return Returns the invOutstandingDays. 257 */ 258 public Integer getInvOutstandingDays() { 259 Timestamp invBillingDateTimestamp = new Timestamp(invoice.getBillingDate().getTime()); 260 Timestamp todayDateTimestamp = new Timestamp(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate().getTime()); 261 double diffInDays = DateUtils.getDifferenceInDays(invBillingDateTimestamp, todayDateTimestamp); 262 invOutstandingDays = new Integer(new KualiDecimal(diffInDays).intValue()); 263 264 return invOutstandingDays; 265 } 266 267 /** 268 * Sets the invOutstandingDays attribute value. 269 * @param invOutstandingDays The invOutstandingDays to set. 270 */ 271 public void setInvOutstandingDays(Integer invOutstandingDays) { 272 this.invOutstandingDays = invOutstandingDays; 273 } 274 275 public void recalculateTotalsBasedOnChangedItemAmount(CustomerCreditMemoDetail customerCreditMemoDetail) { 276 KualiDecimal duplicateCreditMemoItemTotalAmount = customerCreditMemoDetail.getDuplicateCreditMemoItemTotalAmount(); 277 KualiDecimal creditMemoItemTotalAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount(); 278 279 // substract the 'old' item amount, tax amount, and total amount accordingly from totals 280 if (ObjectUtils.isNotNull(duplicateCreditMemoItemTotalAmount)) 281 prepareTotalsForUpdate(duplicateCreditMemoItemTotalAmount,getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerCreditMemoDetail.getCustomerInvoiceDetail())); 282 283 recalculateTotals(creditMemoItemTotalAmount,getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerCreditMemoDetail.getCustomerInvoiceDetail())); 284 285 // update duplicate credit memo item amount with 'new' value 286 customerCreditMemoDetail.setDuplicateCreditMemoItemTotalAmount(creditMemoItemTotalAmount); 287 } 288 289 public void recalculateTotals(CustomerCreditMemoDetail customerCreditMemoDetail) { 290 KualiDecimal duplicateCreditMemoItemTotalAmount = customerCreditMemoDetail.getDuplicateCreditMemoItemTotalAmount(); 291 292 // substract the 'old' item amount, tax amount, and total amount accordingly from totals 293 if (ObjectUtils.isNotNull(duplicateCreditMemoItemTotalAmount)) { 294 prepareTotalsForUpdate(duplicateCreditMemoItemTotalAmount,getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerCreditMemoDetail.getCustomerInvoiceDetail())); 295 customerCreditMemoDetail.setDuplicateCreditMemoItemTotalAmount(null); 296 } 297 } 298 299 protected void prepareTotalsForUpdate(KualiDecimal oldItemAmount, boolean isTaxableItemFlag) { 300 KualiDecimal oldItemTaxAmount = KualiDecimal.ZERO; 301 if (isTaxableItemFlag) 302 oldItemTaxAmount = getTaxService().getTotalSalesTaxAmount(invoice.getBillingDate(), getPostalCode(), oldItemAmount); 303 304 crmTotalItemAmount = crmTotalItemAmount.subtract(oldItemAmount); 305 crmTotalTaxAmount = crmTotalTaxAmount.subtract(oldItemTaxAmount); 306 crmTotalAmount = crmTotalAmount.subtract(oldItemAmount.add(oldItemTaxAmount)); 307 } 308 309 public void resetTotals() { 310 crmTotalItemAmount = KualiDecimal.ZERO; 311 crmTotalTaxAmount = KualiDecimal.ZERO; 312 crmTotalAmount = KualiDecimal.ZERO; 313 } 314 315 public void recalculateTotals(KualiDecimal itemAmount, boolean isTaxableItemFlag) { 316 crmTotalItemAmount = crmTotalItemAmount.add(itemAmount); 317 if (isTaxableItemFlag) 318 crmTotalTaxAmount = crmTotalTaxAmount.add(getTaxService().getTotalSalesTaxAmount(invoice.getBillingDate(), getPostalCode(), itemAmount)); 319 crmTotalAmount = crmTotalItemAmount.add(crmTotalTaxAmount); 320 getDocumentHeader().setFinancialDocumentTotalAmount(crmTotalAmount); 321 } 322 323 /* 324 * populate customer credit memo details based on the invoice info 325 */ 326 public void populateCustomerCreditMemoDetails() { 327 CustomerCreditMemoDetail customerCreditMemoDetail; 328 KualiDecimal invItemTaxAmount, openInvoiceQuantity, openInvoiceAmount; 329 330 CustomerInvoiceDetailService customerInvoiceDetailService = SpringContext.getBean(CustomerInvoiceDetailService.class); 331 setStatusCode(ArConstants.CustomerCreditMemoStatuses.IN_PROCESS); 332 333 //set accounts receivable document header 334 AccountsReceivableDocumentHeader accountsReceivableDocumentHeader = SpringContext.getBean(AccountsReceivableDocumentHeaderService.class).getNewAccountsReceivableDocumentHeaderForCurrentUser(); 335 accountsReceivableDocumentHeader.setDocumentNumber(getDocumentNumber()); 336 accountsReceivableDocumentHeader.setCustomerNumber(invoice.getAccountsReceivableDocumentHeader().getCustomerNumber()); 337 setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader); 338 339 List<CustomerInvoiceDetail> customerInvoiceDetails = invoice.getCustomerInvoiceDetailsWithoutDiscounts(); 340 for (CustomerInvoiceDetail customerInvoiceDetail : customerInvoiceDetails) { 341 customerCreditMemoDetail = new CustomerCreditMemoDetail(); 342 343 if(ObjectUtils.isNull(customerInvoiceDetail.getInvoiceItemTaxAmount())){ 344 customerInvoiceDetail.setInvoiceItemTaxAmount(KualiDecimal.ZERO); 345 } 346 customerCreditMemoDetail.setInvoiceLineTotalAmount(customerInvoiceDetail.getInvoiceItemTaxAmount(), customerInvoiceDetail.getInvoiceItemPreTaxAmount()); 347 customerCreditMemoDetail.setReferenceInvoiceItemNumber(customerInvoiceDetail.getSequenceNumber()); 348 openInvoiceAmount = customerInvoiceDetail.getAmountOpen(); 349 350 customerCreditMemoDetail.setInvoiceOpenItemAmount(openInvoiceAmount); 351 customerCreditMemoDetail.setInvoiceOpenItemQuantity(getInvoiceOpenItemQuantity(customerCreditMemoDetail, customerInvoiceDetail)); 352 customerCreditMemoDetail.setDocumentNumber(this.documentNumber); 353 customerCreditMemoDetail.setFinancialDocumentReferenceInvoiceNumber(this.financialDocumentReferenceInvoiceNumber); 354 355 // this is a hookup for institution custom to update financial object code for prior year(s) invoice 356 //customerInvoiceDetailService.updateFinancialObjectCode(customerCreditMemoDetail); 357 358 creditMemoDetails.add(customerCreditMemoDetail); 359 } 360 361 } 362 363 /** 364 * This method populates credit memo details that aren't saved in database 365 */ 366 public void populateCustomerCreditMemoDetailsAfterLoad() { 367 368 KualiDecimal openInvoiceAmount, invItemTaxAmount, creditMemoItemAmount, creditMemoTaxAmount, taxRate; 369 CustomerInvoiceDetailService customerInvoiceDetailService = SpringContext.getBean(CustomerInvoiceDetailService.class); 370 371 List<CustomerInvoiceDetail> customerInvoiceDetails = getInvoice().getCustomerInvoiceDetailsWithoutDiscounts(); 372 for (CustomerCreditMemoDetail creditMemoDetail : creditMemoDetails) { 373 374 creditMemoDetail.setFinancialDocumentReferenceInvoiceNumber(this.financialDocumentReferenceInvoiceNumber); 375 CustomerInvoiceDetail customerInvoiceDetail = creditMemoDetail.getCustomerInvoiceDetail(); 376 openInvoiceAmount = customerInvoiceDetail.getAmountOpen(); 377 creditMemoDetail.setInvoiceOpenItemAmount(openInvoiceAmount); 378 379 creditMemoDetail.setInvoiceOpenItemQuantity(getInvoiceOpenItemQuantity(creditMemoDetail, customerInvoiceDetail)); 380 381 if(ObjectUtils.isNull(customerInvoiceDetail.getInvoiceItemTaxAmount())){ 382 customerInvoiceDetail.setInvoiceItemTaxAmount(KualiDecimal.ZERO); 383 } 384 creditMemoDetail.setInvoiceLineTotalAmount(customerInvoiceDetail.getInvoiceItemTaxAmount(), customerInvoiceDetail.getInvoiceItemPreTaxAmount()); 385 386 creditMemoItemAmount = creditMemoDetail.getCreditMemoItemTotalAmount(); 387 creditMemoDetail.setDuplicateCreditMemoItemTotalAmount(creditMemoItemAmount); 388 if (ObjectUtils.isNotNull(creditMemoItemAmount)) { 389 if( getArTaxService().isCustomerInvoiceDetailTaxable(invoice, customerInvoiceDetail) ) 390 creditMemoTaxAmount = getTaxService().getTotalSalesTaxAmount(invoice.getBillingDate(), getPostalCode(), creditMemoItemAmount); 391 else 392 creditMemoTaxAmount = KualiDecimal.ZERO; 393 creditMemoDetail.setCreditMemoItemTaxAmount(creditMemoTaxAmount); 394 creditMemoDetail.setCreditMemoLineTotalAmount(creditMemoItemAmount.add(creditMemoTaxAmount)); 395 396 crmTotalItemAmount = crmTotalItemAmount.add(creditMemoItemAmount); 397 crmTotalTaxAmount = crmTotalTaxAmount.add(creditMemoTaxAmount); 398 crmTotalAmount = crmTotalAmount.add(creditMemoItemAmount.add(creditMemoTaxAmount)); 399 } 400 401 // this is a hookup for institution custom to update financial object code for prior year(s) invoice 402 //customerInvoiceDetailService.updateFinancialObjectCode(creditMemoDetail); 403 } 404 } 405 406 public KualiDecimal getInvoiceOpenItemQuantity(CustomerCreditMemoDetail customerCreditMemoDetail,CustomerInvoiceDetail customerInvoiceDetail) { 407 KualiDecimal invoiceOpenItemQuantity; 408 BigDecimal invoiceItemUnitPrice = customerInvoiceDetail.getInvoiceItemUnitPrice(); 409 if (ObjectUtils.isNull(invoiceItemUnitPrice) || invoiceItemUnitPrice.equals(BigDecimal.ZERO)) 410 invoiceOpenItemQuantity = KualiDecimal.ZERO; 411 else { 412 KualiDecimal invoiceOpenItemAmount = customerCreditMemoDetail.getInvoiceOpenItemAmount(); 413 KualiDecimal invoiceOpenItemPretaxAmount = invoiceOpenItemAmount; 414 if( getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), customerInvoiceDetail)) 415 invoiceOpenItemPretaxAmount = getCustomerInvoiceDetailOpenPretaxAmount(invoiceOpenItemAmount); 416 417 invoiceOpenItemQuantity = new KualiDecimal(invoiceOpenItemPretaxAmount.bigDecimalValue().divide(invoiceItemUnitPrice, 4)); 418 } 419 return invoiceOpenItemQuantity; 420 } 421 422 protected KualiDecimal getCustomerInvoiceDetailOpenPretaxAmount(KualiDecimal openAmount) { 423 Date dateOfTransaction = getInvoice().getBillingDate(); 424 KualiDecimal pretaxAmount = SpringContext.getBean(TaxService.class).getPretaxAmount(dateOfTransaction, getPostalCode(), openAmount); 425 426 return pretaxAmount; 427 } 428 429 /** 430 * do all the calculations before the document gets saved 431 * gets called for 'Submit', 'Save', and 'Blanket Approved' 432 * @see org.kuali.rice.kns.document.Document#prepareForSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent) 433 */ 434 public void prepareForSave(KualiDocumentEvent event) { 435 CustomerCreditMemoDocument customerCreditMemoDocument = (CustomerCreditMemoDocument)event.getDocument(); 436 CustomerCreditMemoDocumentService customerCreditMemoDocumentService = SpringContext.getBean(CustomerCreditMemoDocumentService.class); 437 if (event instanceof BlanketApproveDocumentEvent) 438 customerCreditMemoDocumentService.recalculateCustomerCreditMemoDocument(customerCreditMemoDocument,true); 439 else 440 customerCreditMemoDocumentService.recalculateCustomerCreditMemoDocument(customerCreditMemoDocument,false); 441 442 // generate GLPEs 443 if (!SpringContext.getBean(GeneralLedgerPendingEntryService.class).generateGeneralLedgerPendingEntries(this)) { 444 logErrors(); 445 throw new ValidationException("general ledger GLPE generation failed"); 446 } 447 super.prepareForSave(event); 448 } 449 450 /** 451 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#generateDocumentGeneralLedgerPendingEntries(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper) 452 */ 453 public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) { 454 boolean success = true; 455 return success; 456 } 457 458 /** 459 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#isDebit(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) 460 */ 461 public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) { 462 return false; 463 } 464 465 /** 466 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#clearAnyGeneralLedgerPendingEntries() 467 */ 468 public void clearAnyGeneralLedgerPendingEntries() { 469 generalLedgerPendingEntries = new ArrayList<GeneralLedgerPendingEntry>(); 470 } 471 472 /** 473 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#getGeneralLedgerPostables() 474 */ 475 public List<GeneralLedgerPendingEntrySourceDetail> getGeneralLedgerPendingEntrySourceDetails() { 476 List<GeneralLedgerPendingEntrySourceDetail> generalLedgerPendingEntrySourceDetails = new ArrayList<GeneralLedgerPendingEntrySourceDetail>(); 477 if (creditMemoDetails != null) { 478 Iterator iter = creditMemoDetails.iterator(); 479 CustomerCreditMemoDetail customerCreditMemoDetail; 480 KualiDecimal amount; 481 while (iter.hasNext()) { 482 customerCreditMemoDetail = (CustomerCreditMemoDetail)iter.next(); 483 amount = customerCreditMemoDetail.getCreditMemoItemTotalAmount(); 484 485 // get only non empty credit memo details to generate GLPEs 486 if (ObjectUtils.isNotNull(amount) && amount.isGreaterThan(KualiDecimal.ZERO)) 487 generalLedgerPendingEntrySourceDetails.add((GeneralLedgerPendingEntrySourceDetail)customerCreditMemoDetail); 488 } 489 } 490 return generalLedgerPendingEntrySourceDetails; 491 } 492 493 /** 494 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#addPendingEntry(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry) 495 */ 496 public void addPendingEntry(GeneralLedgerPendingEntry entry) { 497 generalLedgerPendingEntries.add(entry); 498 } 499 500 /** 501 * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#getGeneralLedgerPendingEntryAmountForGeneralLedgerPostable(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) 502 */ 503 public KualiDecimal getGeneralLedgerPendingEntryAmountForDetail(GeneralLedgerPendingEntrySourceDetail postable) { 504 return postable.getAmount(); 505 } 506 507 /** 508 * Returns the general ledger input type code for the given document, using the DataDictionaryService 509 * @return the general ledger input type code for the given document 510 */ 511 public String getFinancialDocumentTypeCode() { 512 return SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass()); 513 } 514 515 @Override 516 public List<Long> getWorkflowEngineDocumentIdsToLock() { 517 // a credit memo wont always update the source invoice, but sometimes it will so we include it here 518 if (StringUtils.isNotBlank(getFinancialDocumentReferenceInvoiceNumber())) { 519 List<Long> documentIds = new ArrayList<Long>(); 520 documentIds.add(new Long(getFinancialDocumentReferenceInvoiceNumber())); 521 return documentIds; 522 } 523 return null; 524 } 525 526 /** 527 * When document is processed do the following: 528 * 529 * 1) Apply amounts to writeoff invoice 530 * 2) Mark off invoice indiciator 531 * 532 * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange() 533 */ 534 @Override 535 public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent){ 536 super.doRouteStatusChange(statusChangeEvent); 537 if (getDocumentHeader().getWorkflowDocument().stateIsProcessed()) { 538 539 //have to populate because not all the customer credit memo details are populated while doc is in workflow 540 populateCustomerCreditMemoDetailsAfterLoad(); 541 542 // apply writeoff amounts by only retrieving only the invoice details that ARE NOT discounts 543 CustomerCreditMemoDocumentService service = SpringContext.getBean(CustomerCreditMemoDocumentService.class); 544 service.completeCustomerCreditMemo(this); 545 } 546 } 547 548 /** 549 * This method creates the following GLPE's for the customer credit memo 550 * 551 * 1. Credit to receivable object for total line amount (excluding sales tax) 552 * 2. Debit to income object for total line amount (excluding sales tax) 553 * 3. Credit to receivable object in sales tax account(if sales tax exists) 554 * 4. Debit to liability object in sales tax account(if sales tax exists) 555 * 5. Credit to receivable object in district tax account(if district tax exists) 556 * 6. Debit to liability object code in district tax account(if district tax exists) 557 * 558 * @see org.kuali.kfs.service.impl.GenericGeneralLedgerPendingEntryGenerationProcessImpl#processGenerateGeneralLedgerPendingEntries(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper) 559 */ 560 561 public boolean generateGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntrySequenceHelper sequenceHelper) { 562 563 String receivableOffsetOption = SpringContext.getBean(ParameterService.class).getParameterValue(CustomerInvoiceDocument.class, ArConstants.GLPE_RECEIVABLE_OFFSET_GENERATION_METHOD); 564 boolean hasClaimOnCashOffset = ArConstants.GLPE_RECEIVABLE_OFFSET_GENERATION_METHOD_FAU.equals(receivableOffsetOption); 565 566 addReceivableGLPEs(sequenceHelper, glpeSourceDetail, hasClaimOnCashOffset); 567 sequenceHelper.increment(); 568 addIncomeGLPEs(sequenceHelper, glpeSourceDetail, hasClaimOnCashOffset); 569 570 //if sales tax is enabled generate GLPEs 571 CustomerInvoiceDetail invoiceDetail = ((CustomerCreditMemoDetail) glpeSourceDetail).getCustomerInvoiceDetail(); 572 if( getArTaxService().isCustomerInvoiceDetailTaxable(getInvoice(), invoiceDetail) ) 573 addSalesTaxGLPEs( sequenceHelper, glpeSourceDetail, hasClaimOnCashOffset ); 574 575 return true; 576 } 577 578 /** 579 * This method creates the receivable GLPEs for credit memo detail lines. 580 * 581 * @param poster 582 * @param sequenceHelper 583 * @param postable 584 * @param explicitEntry 585 */ 586 protected void addReceivableGLPEs(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, boolean hasClaimOnCashOffset) { 587 588 CustomerCreditMemoDetail customerCreditMemoDetail = (CustomerCreditMemoDetail)glpeSourceDetail; 589 CustomerInvoiceDetail customerInvoiceDetail = customerCreditMemoDetail.getCustomerInvoiceDetail(); 590 ReceivableCustomerInvoiceDetail receivableCustomerInvoiceDetail = new ReceivableCustomerInvoiceDetail(customerInvoiceDetail, this.getInvoice()); 591 boolean isDebit = false; 592 593 CustomerInvoiceGLPEService service = SpringContext.getBean(CustomerInvoiceGLPEService.class); 594 service.createAndAddGenericInvoiceRelatedGLPEs(this, receivableCustomerInvoiceDetail, sequenceHelper, isDebit, hasClaimOnCashOffset, customerCreditMemoDetail.getCreditMemoItemTotalAmount()); 595 } 596 597 /** 598 * This method adds pending entry with transaction ledger entry amount set to item price * quantity 599 * 600 * @param poster 601 * @param sequenceHelper 602 * @param postable 603 * @param explicitEntry 604 */ 605 protected void addIncomeGLPEs(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, boolean hasClaimOnCashOffset) { 606 607 CustomerCreditMemoDetail customerCreditMemoDetail = (CustomerCreditMemoDetail)glpeSourceDetail; 608 boolean isDebit = true; 609 610 CustomerInvoiceGLPEService service = SpringContext.getBean(CustomerInvoiceGLPEService.class); 611 service.createAndAddGenericInvoiceRelatedGLPEs(this, customerCreditMemoDetail, sequenceHelper, isDebit, hasClaimOnCashOffset, customerCreditMemoDetail.getCreditMemoItemTotalAmount()); 612 } 613 614 /** 615 * This method add pending entries for every tax detail that exists for a particular postal code 616 * 617 * @param sequenceHelper 618 * @param glpeSourceDetail 619 * @param hasClaimOnCashOffset 620 */ 621 protected void addSalesTaxGLPEs(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, boolean hasClaimOnCashOffset){ 622 623 CustomerCreditMemoDetail customerCreditMemoDetail = (CustomerCreditMemoDetail)glpeSourceDetail; 624 boolean isDebit = false; 625 626 String postalCode = getPostalCode(); 627 Date dateOfTransaction = getInvoice().getBillingDate(); 628 629 List<TaxDetail> salesTaxDetails = getTaxService().getSalesTaxDetails(dateOfTransaction, postalCode, customerCreditMemoDetail.getCreditMemoItemTotalAmount()); 630 631 CustomerInvoiceGLPEService service = SpringContext.getBean(CustomerInvoiceGLPEService.class); 632 SalesTaxCustomerCreditMemoDetail salesTaxCustomerCreditMemoDetail; 633 ReceivableCustomerCreditMemoDetail receivableCustomerCreditMemoDetail; 634 for( TaxDetail salesTaxDetail : salesTaxDetails ){ 635 636 salesTaxCustomerCreditMemoDetail = new SalesTaxCustomerCreditMemoDetail( salesTaxDetail, customerCreditMemoDetail ); 637 salesTaxCustomerCreditMemoDetail.setCustomerInvoiceDetail(customerCreditMemoDetail.getCustomerInvoiceDetail()); 638 receivableCustomerCreditMemoDetail = new ReceivableCustomerCreditMemoDetail(salesTaxCustomerCreditMemoDetail, this); 639 receivableCustomerCreditMemoDetail.setCustomerInvoiceDetail(customerCreditMemoDetail.getCustomerInvoiceDetail()); 640 641 sequenceHelper.increment(); 642 service.createAndAddGenericInvoiceRelatedGLPEs(this, receivableCustomerCreditMemoDetail, sequenceHelper, isDebit, hasClaimOnCashOffset, salesTaxDetail.getTaxAmount()); 643 644 sequenceHelper.increment(); 645 service.createAndAddGenericInvoiceRelatedGLPEs(this, salesTaxCustomerCreditMemoDetail, sequenceHelper, !isDebit, hasClaimOnCashOffset, salesTaxDetail.getTaxAmount()); 646 } 647 } 648 649 public KualiDecimal getTotalDollarAmount() { 650 return getDocumentHeader().getFinancialDocumentTotalAmount(); 651 } 652 653 654 public AccountsReceivableDocumentHeader getAccountsReceivableDocumentHeader() { 655 return accountsReceivableDocumentHeader; 656 } 657 658 public void setAccountsReceivableDocumentHeader(AccountsReceivableDocumentHeader accountsReceivableDocumentHeader) { 659 this.accountsReceivableDocumentHeader = accountsReceivableDocumentHeader; 660 } 661 662 public String getPostalCode() { 663 String postalCode = getArTaxService().getPostalCodeForTaxation(getInvoice()); 664 return postalCode; 665 } 666 667 /** 668 * Gets the taxService attribute. 669 * @return Returns the taxService. 670 */ 671 public TaxService getTaxService() { 672 // lazy init the service if its been nullified by session-izing the document 673 if (taxService == null) taxService = SpringContext.getBean(TaxService.class); 674 return taxService; 675 } 676 677 /** 678 * Gets the arTaxService attribute. 679 * @return Returns the arTaxService. 680 */ 681 public AccountsReceivableTaxService getArTaxService() { 682 // lazy init the service if its been nullified by session-izing the document 683 if (arTaxService == null) arTaxService = SpringContext.getBean(AccountsReceivableTaxService.class); 684 return arTaxService; 685 } 686 687 }