View Javadoc
1   /*
2    * Copyright 2007 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  
17  package org.kuali.ole.module.purap.document;
18  
19  import org.kuali.ole.module.purap.PurapConstants;
20  import org.kuali.ole.module.purap.PurapConstants.PurapDocTypeCodes;
21  import org.kuali.ole.module.purap.PurapConstants.PurchaseOrderStatuses;
22  import org.kuali.ole.module.purap.PurapWorkflowConstants;
23  import org.kuali.ole.module.purap.businessobject.PurchaseOrderAccount;
24  import org.kuali.ole.module.purap.businessobject.PurchaseOrderItem;
25  import org.kuali.ole.module.purap.document.service.PurapService;
26  import org.kuali.ole.module.purap.document.service.PurchaseOrderService;
27  import org.kuali.ole.module.purap.document.service.ReceivingService;
28  import org.kuali.ole.module.purap.service.PurapAccountingService;
29  import org.kuali.ole.module.purap.service.PurapGeneralLedgerService;
30  import org.kuali.ole.sys.OLEConstants;
31  import org.kuali.ole.sys.businessobject.AccountingLine;
32  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry;
33  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
34  import org.kuali.ole.sys.businessobject.SourceAccountingLine;
35  import org.kuali.ole.sys.context.SpringContext;
36  import org.kuali.ole.sys.document.AccountingDocument;
37  import org.kuali.rice.core.api.util.type.KualiDecimal;
38  import org.kuali.rice.kew.api.exception.WorkflowException;
39  import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
40  import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
41  import org.kuali.rice.krad.util.ObjectUtils;
42  import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
43  
44  import java.math.BigDecimal;
45  import java.util.ArrayList;
46  import java.util.Collections;
47  import java.util.Iterator;
48  import java.util.List;
49  
50  import static org.kuali.ole.sys.OLEConstants.GL_DEBIT_CODE;
51  import static org.kuali.rice.core.api.util.type.KualiDecimal.ZERO;
52  
53  /**
54   * Purchase Order Amendment Document
55   */
56  public class PurchaseOrderAmendmentDocument extends PurchaseOrderDocument {
57      protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurchaseOrderAmendmentDocument.class);
58  
59      boolean newUnorderedItem; //Used for routing
60      String receivingDeliveryCampusCode; //Used for routing
61  
62      /**
63       * When Purchase Order Amendment document has been Processed through Workflow, the general ledger entries are created and the PO
64       * status remains "OPEN".
65       *
66       * @see org.kuali.ole.module.purap.document.PurchaseOrderDocument#doRouteStatusChange()
67       */
68      @Override
69      public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
70          super.doRouteStatusChange(statusChangeEvent);
71  
72          try {
73              // DOCUMENT PROCESSED
74              if (this.getFinancialSystemDocumentHeader().getWorkflowDocument().isProcessed()) {
75                  // generate GL entries
76                  SpringContext.getBean(PurapGeneralLedgerService.class).generateEntriesApproveAmendPurchaseOrder(this);
77  
78                  // if gl entries created(means there is amount change) for amend purchase order send an FYI to all fiscal officers
79                  if ((getGlOnlySourceAccountingLines() != null && !getGlOnlySourceAccountingLines().isEmpty())) {
80                      SpringContext.getBean(PurchaseOrderService.class).sendFyiForGLEntries(this);
81                  }
82  
83                  // update indicators
84                  SpringContext.getBean(PurchaseOrderService.class).completePurchaseOrderAmendment(this);
85  
86                  // update vendor commodity code by automatically spawning vendor maintenance document
87                  SpringContext.getBean(PurchaseOrderService.class).updateVendorCommodityCode(this);
88  
89                  // for app doc status
90                  updateAndSaveAppDocStatus(PurchaseOrderStatuses.APPDOC_OPEN);
91              }
92              // DOCUMENT DISAPPROVED
93              else if (this.getFinancialSystemDocumentHeader().getWorkflowDocument().isDisapproved()) {
94                  SpringContext.getBean(PurchaseOrderService.class).setCurrentAndPendingIndicatorsForDisapprovedChangePODocuments(this);
95                  SpringContext.getBean(PurapService.class).saveDocumentNoValidation(this);
96  
97                  // for app doc status
98                  try {
99                      String nodeName = SpringContext.getBean(WorkflowDocumentService.class).getCurrentRouteLevelName(this.getFinancialSystemDocumentHeader().getWorkflowDocument());
100                     String reqStatus = PurapConstants.PurchaseOrderStatuses.getPurchaseOrderAppDocDisapproveStatuses().get(nodeName);
101                     updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.getPurchaseOrderAppDocDisapproveStatuses().get(reqStatus));
102 
103                     RequisitionDocument req = getPurApSourceDocumentIfPossible();
104                     appSpecificRouteDocumentToUser(this.getFinancialSystemDocumentHeader().getWorkflowDocument(), req.getFinancialSystemDocumentHeader().getWorkflowDocument().getRoutedByPrincipalId(), "Notification of Order Disapproval for Requisition " + req.getPurapDocumentIdentifier() + "(document id " + req.getDocumentNumber() + ")", "Requisition Routed By User");
105                     return;
106 
107                 } catch (WorkflowException e) {
108                     logAndThrowRuntimeException("Error saving routing data while saving App Doc Status " + getDocumentNumber(), e);
109                 }
110             }
111             // DOCUMENT CANCELED
112             else if (this.getFinancialSystemDocumentHeader().getWorkflowDocument().isCanceled()) {
113                 SpringContext.getBean(PurchaseOrderService.class).setCurrentAndPendingIndicatorsForCancelledChangePODocuments(this);
114 
115                 // for app doc status
116                 updateAndSaveAppDocStatus(PurapConstants.PurchaseOrderStatuses.APPDOC_CANCELLED);
117             }
118         } catch (WorkflowException e) {
119             logAndThrowRuntimeException("Error saving routing data while saving document with id " + getDocumentNumber(), e);
120         }
121     }
122 
123     /**
124      * @see org.kuali.module.purap.rules.PurapAccountingDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.ole.sys.document.AccountingDocument,
125      *      org.kuali.ole.sys.businessobject.AccountingLine, org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry)
126      */
127     @Override
128     public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) {
129         //super.customizeExplicitGeneralLedgerPendingEntry(postable, explicitEntry);
130 
131         SpringContext.getBean(PurapGeneralLedgerService.class).customizeGeneralLedgerPendingEntry(this, (AccountingLine) postable, explicitEntry, getPurapDocumentIdentifier(), GL_DEBIT_CODE, PurapDocTypeCodes.PO_DOCUMENT, true);
132 
133         // don't think i should have to override this, but default isn't getting the right PO doc
134         explicitEntry.setFinancialDocumentTypeCode(PurapDocTypeCodes.PO_AMENDMENT_DOCUMENT);
135         explicitEntry.setFinancialDocumentApprovedCode(OLEConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.APPROVED);
136     }
137 
138     @Override
139     public List<GeneralLedgerPendingEntrySourceDetail> getGeneralLedgerPendingEntrySourceDetails() {
140         List<GeneralLedgerPendingEntrySourceDetail> accountingLines = new ArrayList<GeneralLedgerPendingEntrySourceDetail>();
141         if (getGlOnlySourceAccountingLines() != null) {
142             Iterator iter = getGlOnlySourceAccountingLines().iterator();
143             while (iter.hasNext()) {
144                 accountingLines.add((GeneralLedgerPendingEntrySourceDetail) iter.next());
145             }
146         }
147         return accountingLines;
148     }
149 
150 
151     @Override
152     public void populateDocumentForRouting() {
153         newUnorderedItem = SpringContext.getBean(PurchaseOrderService.class).hasNewUnorderedItem(this);
154         receivingDeliveryCampusCode = SpringContext.getBean(ReceivingService.class).getReceivingDeliveryCampusCode(this);
155         super.populateDocumentForRouting();
156     }
157 
158     public boolean isNewUnorderedItem() {
159         return newUnorderedItem;
160     }
161 
162     public void setNewUnorderedItem(boolean newUnorderedItem) {
163         this.newUnorderedItem = newUnorderedItem;
164     }
165 
166     public String getReceivingDeliveryCampusCode() {
167         return receivingDeliveryCampusCode;
168     }
169 
170     public void setReceivingDeliveryCampusCode(String receivingDeliveryCampusCode) {
171         this.receivingDeliveryCampusCode = receivingDeliveryCampusCode;
172     }
173 
174     @Override
175     public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
176         if (nodeName.equals(PurapWorkflowConstants.HAS_NEW_UNORDERED_ITEMS)) {
177             return isNewUnorderedItem();
178         }
179         throw new UnsupportedOperationException("Cannot answer split question for this node you call \"" + nodeName + "\"");
180     }
181 
182     // MSU Contribution OLEMI-8600 DTT-3159 OLECNTRB-953
183     @Override
184     public Class<? extends AccountingDocument> getDocumentClassForAccountingLineValueAllowedValidation() {
185         return PurchaseOrderDocument.class;
186     }
187 
188     // MSU Contribution DTT-3812 OLEMI-8642 OLECNTRB-957
189     @Override
190     public void customPrepareForSave(KualiDocumentEvent event) {
191         super.customPrepareForSave(event);
192 
193         // Set outstanding encumbered quantity/amount on items
194         List<PurchaseOrderItem> poItems = this.getItems();
195         for (PurchaseOrderItem item : poItems) {
196             if (item.isItemActiveIndicator()) {
197                 KualiDecimal invoicedTotalQuantity = item.getItemInvoicedTotalQuantity();
198                 if (item.getItemInvoicedTotalQuantity() == null) {
199                     invoicedTotalQuantity = ZERO;
200                     item.setItemInvoicedTotalQuantity(ZERO);
201                 }
202 
203                 if (item.getItemInvoicedTotalAmount() == null) {
204                     item.setItemInvoicedTotalAmount(ZERO);
205                 }
206                 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
207                     KualiDecimal itemQuantity = item.getItemQuantity() == null ? ZERO : item.getItemQuantity();
208                     KualiDecimal outstandingEncumberedQuantity = itemQuantity.subtract(invoicedTotalQuantity);
209                     item.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity);
210                 }
211                 KualiDecimal outstandingEncumbrance = null;
212 
213                 // If the item is amount based (not quantity based)
214                 if (item.getItemType().isAmountBasedGeneralLedgerIndicator()) {
215                     KualiDecimal totalAmount = item.getTotalAmount() == null ? ZERO : item.getTotalAmount();
216                     outstandingEncumbrance = totalAmount.subtract(item.getItemInvoicedTotalAmount());
217                     item.setItemOutstandingEncumberedAmount(outstandingEncumbrance);
218                 } else {
219                     // if the item is quantity based
220                     BigDecimal itemUnitPrice = ObjectUtils.isNull(item.getItemUnitPrice()) ? BigDecimal.ZERO : item
221                             .getItemUnitPrice();
222                     outstandingEncumbrance = new KualiDecimal(item.getItemOutstandingEncumberedQuantity()
223                             .bigDecimalValue().multiply(itemUnitPrice));
224                     item.setItemOutstandingEncumberedAmount(outstandingEncumbrance);
225                 }
226 
227                 List accountLines = item.getSourceAccountingLines();
228                 Collections.sort(accountLines);
229 
230                 updateEncumbranceOnAccountingLines(outstandingEncumbrance, accountLines);
231             }
232         }
233 
234         List<SourceAccountingLine> sourceLines = SpringContext.getBean(PurapAccountingService.class)
235                 .generateSummaryWithNoZeroTotals(this.getItems());
236         this.setSourceAccountingLines(sourceLines);
237     }
238 
239     // MSU Contribution DTT-3812 OLEMI-8642 OLECNTRB-957
240     protected void updateEncumbranceOnAccountingLines(KualiDecimal outstandingEcumbrance, List accountLines) {
241         for (Object accountLineObject : accountLines) {
242             PurchaseOrderAccount accountLine = (PurchaseOrderAccount) accountLineObject;
243             if (!accountLine.isEmpty()) {
244                 BigDecimal linePercent = accountLine.getAccountLinePercent();
245                 KualiDecimal accountOutstandingEncumbrance = new KualiDecimal(outstandingEcumbrance.bigDecimalValue()
246                         .multiply(linePercent).divide(OLEConstants.ONE_HUNDRED.bigDecimalValue()));
247                 accountLine.setItemAccountOutstandingEncumbranceAmount(accountOutstandingEncumbrance);
248             }
249         }
250     }
251 }