1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.kuali.kfs.module.ar.document.validation.impl;
20
21 import java.math.BigDecimal;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.apache.commons.lang.StringUtils;
28 import org.kuali.kfs.module.ar.ArConstants;
29 import org.kuali.kfs.module.ar.ArKeyConstants;
30 import org.kuali.kfs.module.ar.ArPropertyConstants;
31 import org.kuali.kfs.module.ar.businessobject.CustomerCreditMemoDetail;
32 import org.kuali.kfs.module.ar.document.CustomerCreditMemoDocument;
33 import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument;
34 import org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService;
35 import org.kuali.kfs.module.ar.document.validation.ContinueCustomerCreditMemoDocumentRule;
36 import org.kuali.kfs.module.ar.document.validation.RecalculateCustomerCreditMemoDetailRule;
37 import org.kuali.kfs.module.ar.document.validation.RecalculateCustomerCreditMemoDocumentRule;
38 import org.kuali.kfs.sys.KFSConstants;
39 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
40 import org.kuali.kfs.sys.context.SpringContext;
41 import org.kuali.rice.core.api.util.type.KualiDecimal;
42 import org.kuali.rice.kew.api.WorkflowDocument;
43 import org.kuali.rice.kew.api.WorkflowDocumentFactory;
44 import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
45 import org.kuali.rice.krad.document.Document;
46 import org.kuali.rice.krad.document.TransactionalDocument;
47 import org.kuali.rice.krad.service.BusinessObjectService;
48 import org.kuali.rice.krad.util.GlobalVariables;
49 import org.kuali.rice.krad.util.KRADConstants;
50 import org.kuali.rice.krad.util.ObjectUtils;
51
52
53
54
55
56 public class CustomerCreditMemoDocumentRule extends TransactionalDocumentRuleBase implements RecalculateCustomerCreditMemoDetailRule<TransactionalDocument>, RecalculateCustomerCreditMemoDocumentRule<TransactionalDocument>, ContinueCustomerCreditMemoDocumentRule<TransactionalDocument> {
57
58 protected static final BigDecimal ALLOWED_QTY_DEVIATION = new BigDecimal("0.10");
59
60 public CustomerCreditMemoDocumentRule() {
61 }
62
63
64
65
66 @Override
67 protected boolean processCustomSaveDocumentBusinessRules(Document document) {
68 boolean isValid = super.processCustomSaveDocumentBusinessRules(document);
69
70 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
71 isValid &= processRecalculateCustomerCreditMemoDocumentRules((TransactionalDocument) document, true);
72 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME);
73
74 return isValid;
75 }
76
77
78
79
80
81 @Override
82 public boolean processRecalculateCustomerCreditMemoDetailRules(TransactionalDocument document, CustomerCreditMemoDetail customerCreditMemoDetail) {
83 boolean success = true;
84
85 CustomerCreditMemoDocument customerCreditMemoDocument = (CustomerCreditMemoDocument) document;
86 customerCreditMemoDocument.refreshReferenceObject("invoice");
87 String inputKey = isQtyOrItemAmountEntered(customerCreditMemoDetail);
88
89
90 customerCreditMemoDetail.setInvoiceOpenItemAmount(customerCreditMemoDetail.getCustomerInvoiceDetail().getAmountOpen());
91 customerCreditMemoDetail.setInvoiceOpenItemQuantity(customerCreditMemoDocument.getInvoiceOpenItemQuantity(customerCreditMemoDetail, customerCreditMemoDetail.getCustomerInvoiceDetail()));
92
93
94 if (StringUtils.equals(ArConstants.CustomerCreditMemoConstants.CUSTOMER_CREDIT_MEMO_ITEM_QUANTITY, inputKey)) {
95 success &= isValueGreaterThanZero(customerCreditMemoDetail.getCreditMemoItemQuantity());
96 success &= isCustomerCreditMemoQtyLessThanEqualToInvoiceOpenQty(customerCreditMemoDetail);
97 }
98
99 else if (StringUtils.equals(ArConstants.CustomerCreditMemoConstants.CUSTOMER_CREDIT_MEMO_ITEM_TOTAL_AMOUNT, inputKey)) {
100 success &= isValueGreaterThanZero(customerCreditMemoDetail.getCreditMemoItemTotalAmount());
101 success &= isCustomerCreditMemoItemAmountLessThanEqualToInvoiceOpenItemAmount(customerCreditMemoDocument, customerCreditMemoDetail);
102 }
103
104 else if (StringUtils.equals(ArConstants.CustomerCreditMemoConstants.BOTH_QUANTITY_AND_ITEM_TOTAL_AMOUNT_ENTERED, inputKey)) {
105 success &= isValueGreaterThanZero(customerCreditMemoDetail.getCreditMemoItemTotalAmount());
106 success &= isCustomerCreditMemoItemAmountLessThanEqualToInvoiceOpenItemAmount(customerCreditMemoDocument, customerCreditMemoDetail);
107 success &= isValueGreaterThanZero(customerCreditMemoDetail.getCreditMemoItemQuantity());
108 success &= isCustomerCreditMemoQtyLessThanEqualToInvoiceOpenQty(customerCreditMemoDetail);
109 success &= checkIfCustomerCreditMemoQtyAndCustomerCreditMemoItemAmountValid(customerCreditMemoDetail, customerCreditMemoDetail.getCustomerInvoiceDetail().getInvoiceItemUnitPrice());
110 }
111
112 else {
113 success = false;
114 }
115 return success;
116 }
117
118 public String isQtyOrItemAmountEntered(CustomerCreditMemoDetail customerCreditMemoDetail) {
119
120 BigDecimal customerCreditMemoItemQty = customerCreditMemoDetail.getCreditMemoItemQuantity();
121 KualiDecimal customerCreditMemoItemAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount();
122 String inputKey = "";
123
124 if (ObjectUtils.isNotNull(customerCreditMemoItemQty) && ObjectUtils.isNotNull(customerCreditMemoItemAmount)) {
125 inputKey = ArConstants.CustomerCreditMemoConstants.BOTH_QUANTITY_AND_ITEM_TOTAL_AMOUNT_ENTERED;
126 }
127 else if (ObjectUtils.isNotNull(customerCreditMemoItemQty)) {
128 inputKey = ArConstants.CustomerCreditMemoConstants.CUSTOMER_CREDIT_MEMO_ITEM_QUANTITY;
129 }
130 else if (ObjectUtils.isNotNull(customerCreditMemoItemAmount)) {
131 inputKey = ArConstants.CustomerCreditMemoConstants.CUSTOMER_CREDIT_MEMO_ITEM_TOTAL_AMOUNT;
132 }
133
134 return inputKey;
135 }
136
137 public boolean isValueGreaterThanZero(BigDecimal value) {
138 boolean validValue = (value.compareTo(BigDecimal.ZERO) == 1 ? true : false);
139 if (!validValue) {
140 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_ITEM_QUANTITY, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DETAIL_ITEM_QUANTITY_LESS_THAN_OR_EQUAL_TO_ZERO);
141 }
142 return validValue;
143 }
144
145 public boolean isValueGreaterThanZero(KualiDecimal value) {
146 boolean validValue = value.isPositive();
147 if (!validValue) {
148 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_ITEM_TOTAL_AMOUNT, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DETAIL_ITEM_AMOUNT_LESS_THAN_OR_EQUAL_TO_ZERO);
149 }
150 return validValue;
151 }
152
153 public boolean isCustomerCreditMemoItemAmountLessThanEqualToInvoiceOpenItemAmount(CustomerCreditMemoDocument customerCreditMemoDocument, CustomerCreditMemoDetail customerCreditMemoDetail) {
154
155 KualiDecimal invoiceOpenItemAmount = customerCreditMemoDetail.getInvoiceOpenItemAmount();
156 KualiDecimal creditMemoItemAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount();
157
158 boolean validItemAmount = creditMemoItemAmount.isLessEqual(invoiceOpenItemAmount);
159 if (!validItemAmount) {
160 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_ITEM_TOTAL_AMOUNT, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DETAIL_ITEM_AMOUNT_GREATER_THAN_INVOICE_ITEM_AMOUNT);
161 }
162
163 return validItemAmount;
164 }
165
166 public boolean isCustomerCreditMemoQtyLessThanEqualToInvoiceOpenQty(CustomerCreditMemoDetail customerCreditMemoDetail) {
167 BigDecimal invoiceOpenItemQty = customerCreditMemoDetail.getInvoiceOpenItemQuantity();
168 BigDecimal customerCreditMemoItemQty = customerCreditMemoDetail.getCreditMemoItemQuantity();
169
170
171 boolean validQuantity = (customerCreditMemoItemQty.compareTo(invoiceOpenItemQty) < 1 ? true : false);
172 if (!validQuantity) {
173 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_ITEM_QUANTITY, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DETAIL_ITEM_QUANTITY_GREATER_THAN_INVOICE_ITEM_QUANTITY);
174 }
175
176 return validQuantity;
177 }
178
179 public boolean checkIfCustomerCreditMemoQtyAndCustomerCreditMemoItemAmountValid(CustomerCreditMemoDetail customerCreditMemoDetail, BigDecimal unitPrice) {
180 KualiDecimal creditAmount = customerCreditMemoDetail.getCreditMemoItemTotalAmount();
181 BigDecimal creditQuantity = customerCreditMemoDetail.getCreditMemoItemQuantity();
182
183
184 if (unitPrice.compareTo(BigDecimal.ZERO) == 0) {
185
186 return false;
187 }
188
189
190 BigDecimal expectedCreditQuantity = creditAmount.bigDecimalValue().divide(unitPrice, ArConstants.ITEM_QUANTITY_SCALE, BigDecimal.ROUND_HALF_UP);
191
192
193 if (expectedCreditQuantity.compareTo(BigDecimal.ZERO) == 0 && creditQuantity.compareTo(BigDecimal.ZERO) != 0) {
194 return false;
195 }
196
197
198 BigDecimal deviationPercentage = creditQuantity.subtract(expectedCreditQuantity).divide(expectedCreditQuantity, ArConstants.ITEM_QUANTITY_SCALE, BigDecimal.ROUND_HALF_UP).abs();
199
200
201 boolean validFlag = deviationPercentage.compareTo(ALLOWED_QTY_DEVIATION) < 1;
202
203 if (!validFlag) {
204 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_ITEM_QUANTITY, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DETAIL_INVALID_DATA_INPUT);
205 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_ITEM_TOTAL_AMOUNT, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DETAIL_INVALID_DATA_INPUT);
206 }
207 return validFlag;
208 }
209
210
211
212
213 @Override
214 public boolean processRecalculateCustomerCreditMemoDocumentRules(TransactionalDocument document, boolean printErrMsgFlag) {
215 boolean success = true;
216 boolean crmDataEnteredFlag = false;
217 CustomerCreditMemoDocument customerCreditMemoDocument = (CustomerCreditMemoDocument) document;
218 List<CustomerCreditMemoDetail> customerCreditMemoDetails = customerCreditMemoDocument.getCreditMemoDetails();
219 int i = 0;
220 String propertyName;
221
222 for (CustomerCreditMemoDetail customerCreditMemoDetail : customerCreditMemoDetails) {
223 propertyName = KFSConstants.CUSTOMER_CREDIT_MEMO_DETAIL_PROPERTY_NAME + "[" + i + "]";
224 GlobalVariables.getMessageMap().addToErrorPath(propertyName);
225
226
227 if (!isQtyOrItemAmountEntered(customerCreditMemoDetail).equals(StringUtils.EMPTY)) {
228 crmDataEnteredFlag = true;
229 success &= processRecalculateCustomerCreditMemoDetailRules(customerCreditMemoDocument, customerCreditMemoDetail);
230 }
231 GlobalVariables.getMessageMap().removeFromErrorPath(propertyName);
232 i++;
233 }
234
235 success &= crmDataEnteredFlag;
236
237
238 if (!crmDataEnteredFlag && printErrMsgFlag) {
239 GlobalVariables.getMessageMap().putError(KFSConstants.DOCUMENT_PROPERTY_NAME, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT_NO_DATA_TO_SUBMIT);
240 }
241
242 return success;
243 }
244
245
246
247
248 @Override
249 public boolean processContinueCustomerCreditMemoDocumentRules(TransactionalDocument document) {
250 boolean success;
251 CustomerCreditMemoDocument customerCreditMemoDocument = (CustomerCreditMemoDocument) document;
252
253 success = checkIfInvoiceNumberIsFinal(customerCreditMemoDocument.getFinancialDocumentReferenceInvoiceNumber());
254 if (success) {
255 success = checkIfThereIsNoAnotherCRMInRouteForTheInvoice(customerCreditMemoDocument.getFinancialDocumentReferenceInvoiceNumber());
256 }
257 if (success) {
258 success = checkInvoiceForErrorCorrection(customerCreditMemoDocument.getFinancialDocumentReferenceInvoiceNumber());
259 }
260
261 return success;
262 }
263
264 public boolean checkIfInvoiceNumberIsFinal(String invDocumentNumber) {
265 boolean success = true;
266
267 if (StringUtils.isBlank(invDocumentNumber)) {
268 success &= false;
269 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT__INVOICE_DOCUMENT_NUMBER_IS_REQUIRED);
270 }
271 else {
272 CustomerInvoiceDocumentService service = SpringContext.getBean(CustomerInvoiceDocumentService.class);
273 CustomerInvoiceDocument customerInvoiceDocument = service.getInvoiceByInvoiceDocumentNumber(invDocumentNumber);
274 if (ObjectUtils.isNull(customerInvoiceDocument)) {
275 success &= false;
276 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT_INVALID_INVOICE_DOCUMENT_NUMBER);
277 }
278 else if (!SpringContext.getBean(CustomerInvoiceDocumentService.class).checkIfInvoiceNumberIsFinal(invDocumentNumber)) {
279 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_INVOICE_DOCUMENT_NOT_FINAL);
280 success &= false;
281 }
282 }
283 return success;
284 }
285
286
287
288
289
290
291
292
293 public boolean checkIfThereIsNoAnotherCRMInRouteForTheInvoice(String invoiceDocumentNumber) {
294
295 WorkflowDocument workflowDocument;
296 boolean success = true;
297
298 Map<String, String> fieldValues = new HashMap<String, String>();
299 fieldValues.put("financialDocumentReferenceInvoiceNumber", invoiceDocumentNumber);
300
301 BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
302 Collection<CustomerCreditMemoDocument> customerCreditMemoDocuments = businessObjectService.findMatching(CustomerCreditMemoDocument.class, fieldValues);
303
304
305 if (customerCreditMemoDocuments.isEmpty()) {
306 return success;
307 }
308
309 String userId = GlobalVariables.getUserSession().getPrincipalId();
310 for(CustomerCreditMemoDocument customerCreditMemoDocument : customerCreditMemoDocuments) {
311 workflowDocument = WorkflowDocumentFactory.loadDocument(userId, customerCreditMemoDocument.getDocumentNumber());
312 if (!(workflowDocument.isApproved() || workflowDocument.isProcessed() || workflowDocument.isCanceled() || workflowDocument.isDisapproved())) {
313 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT_ONE_CRM_IN_ROUTE_PER_INVOICE);
314 success = false;
315 break;
316 }
317 }
318 return success;
319 }
320
321
322
323
324
325
326
327 public boolean checkInvoiceForErrorCorrection(String invoiceDocumentNumber) {
328 CustomerInvoiceDocumentService service = SpringContext.getBean(CustomerInvoiceDocumentService.class);
329 CustomerInvoiceDocument customerInvoiceDocument = service.getInvoiceByInvoiceDocumentNumber(invoiceDocumentNumber);
330
331
332 if (customerInvoiceDocument.hasInvoiceBeenCorrected()) {
333 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT_CORRECTED_INVOICE);
334 return false;
335 }
336
337 if (customerInvoiceDocument.isInvoiceReversal()) {
338 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CustomerCreditMemoDocumentFields.CREDIT_MEMO_DOCUMENT_REF_INVOICE_NUMBER, ArKeyConstants.ERROR_CUSTOMER_CREDIT_MEMO_DOCUMENT_CORRECTING_INVOICE);
339 return false;
340 }
341 return true;
342 }
343
344 @Override
345 public boolean isDocumentAttributesValid(Document document, boolean validateRequired) {
346
347 CustomerCreditMemoDocument customerCreditMemoDocument = (CustomerCreditMemoDocument) document;
348
349 for (CustomerCreditMemoDetail customerDetail : customerCreditMemoDocument.getCreditMemoDetails()) {
350 customerDetail.getCustomerInvoiceDetail().refreshNonUpdateableReferences();
351 }
352
353 for (GeneralLedgerPendingEntry glpe : customerCreditMemoDocument.getGeneralLedgerPendingEntries()) {
354 glpe.refreshNonUpdateableReferences();
355 }
356
357 return super.isDocumentAttributesValid(document, validateRequired);
358 }
359
360 }