1 package org.kuali.ole.deliver.controller.checkout;
2
3 import org.apache.commons.collections.CollectionUtils;
4 import org.apache.commons.lang3.StringUtils;
5 import org.apache.log4j.Logger;
6 import org.kuali.ole.OLEConstants;
7 import org.kuali.ole.deliver.bo.*;
8 import org.kuali.ole.deliver.drools.DroolsConstants;
9 import org.kuali.ole.deliver.drools.DroolsExchange;
10 import org.kuali.ole.deliver.service.OleDeliverRequestDocumentHelperServiceImpl;
11 import org.kuali.ole.deliver.util.DroolsResponse;
12 import org.kuali.ole.deliver.util.ItemInfoUtil;
13 import org.kuali.ole.deliver.util.NoticeInfo;
14 import org.kuali.ole.deliver.util.OleItemRecordForCirc;
15 import org.kuali.ole.docstore.engine.service.storage.rdbms.pojo.ItemRecord;
16 import org.kuali.ole.utility.OleStopWatch;
17 import org.kuali.rice.core.api.config.property.ConfigContext;
18
19 import java.sql.Timestamp;
20 import java.util.*;
21
22
23
24
25 public abstract class CheckoutBaseController extends CircUtilController {
26
27 private static final Logger LOG = Logger.getLogger(CheckoutBaseController.class);
28
29 private OleLoanDocument currentLoanDocument;
30 private NoticeInfo noticeInfo;
31
32
33 public abstract ItemRecord getItemRecord(DroolsExchange droolsExchange);
34
35 public abstract String getOperatorId(DroolsExchange droolsExchange);
36
37 public abstract void setItemRecord(DroolsExchange droolsExchange, ItemRecord itemRecord);
38
39 public abstract String getItemBarcode(DroolsExchange droolsExchange);
40
41 public abstract void setItemBarcode(DroolsExchange droolsExchange, String itemBarcode);
42
43 public abstract OlePatronDocument getCurrentBorrower(DroolsExchange droolsExchange);
44
45 public abstract void setItemValidationDone(boolean result, DroolsExchange droolsExchange);
46
47 public abstract OleCirculationDesk getSelectedCirculationDesk(DroolsExchange droolsExchange);
48
49 public abstract void addLoanDocumentToCurrentSession(OleLoanDocument oleLoanDocument, DroolsExchange droolsExchange);
50
51 public abstract boolean processCustomDueDateIfSet(DroolsExchange droolsExchange, OleLoanDocument oleLoanDocument);
52
53 public abstract void setDueDateTimeForItemRecord(DroolsExchange droolsExchange, Timestamp loanDueDate);
54
55 public abstract void addCurrentLoandDocumentToListofLoandedToPatron(DroolsExchange droolsExchange, OleLoanDocument oleLoanDocument);
56
57 public abstract void removeCurrentLoanDocumentFromCurrentSession(DroolsExchange circForm, OleLoanDocument oleLoanDocument);
58
59 public abstract void clearCurrentSessionList(DroolsExchange droolsExchange);
60
61 public abstract String getCirculationLocationId(DroolsExchange droolsExchange);
62
63
64 public DroolsResponse lookupItemAndSaveLoan(DroolsExchange droolsExchange) {
65
66 DroolsResponse droolsResponse = new DroolsResponse();
67
68 String itemBarcode = getItemBarcode(droolsExchange);
69
70 ItemRecord itemRecord = getItemRecordByBarcode(itemBarcode);
71
72 if (StringUtils.isNotBlank(itemBarcode) && null != itemRecord) {
73 setItemRecord(droolsExchange, itemRecord);
74 OleItemRecordForCirc oleItemRecordForCirc = ItemInfoUtil.getInstance().getOleItemRecordForCirc(itemRecord);
75 if (!preValidationForCheckout(oleItemRecordForCirc, droolsResponse)) {
76 setItemBarcode(droolsExchange, null);
77 droolsResponse.addErrorMessageCode(DroolsConstants.GENERAL_INFO);
78 return droolsResponse;
79 }
80 if (!isNotDamaged(itemRecord)) {
81 droolsResponse = new DroolsResponse();
82 droolsResponse.addErrorMessage("1. Inform the current borrower that this item is damaged.<br/> 2. <b>This item has damage note: </b>" + itemRecord.getDamagedItemNote());
83 droolsResponse.addErrorMessageCode(DroolsConstants.GENERAL_MESSAGE_FLAG);
84 } else if (!isNotClaimsReturned(itemRecord)) {
85 droolsResponse = new DroolsResponse();
86 droolsResponse.addErrorMessage("Already Claims return notes created for this item : " + itemBarcode);
87 droolsResponse.addErrorMessageCode(DroolsConstants.GENERAL_MESSAGE_FLAG);
88 } else {
89 droolsResponse = proceedWithItemValidation(droolsExchange);
90 }
91 } else {
92 droolsResponse = new DroolsResponse();
93 droolsResponse.addErrorMessage("Invalid item barcode : " + itemBarcode);
94 droolsResponse.addErrorMessageCode(DroolsConstants.GENERAL_MESSAGE_FLAG);
95 }
96 return droolsResponse;
97 }
98
99 private boolean preValidationForCheckout(OleItemRecordForCirc oleItemRecordForCirc, DroolsResponse droolsResponse) {
100 if (StringUtils.isBlank(oleItemRecordForCirc.getItemFullPathLocation())) {
101 droolsResponse.addErrorMessage(ConfigContext.getCurrentContextConfig().getProperty(OLEConstants.INVAL_LOC));
102 return false;
103 }
104 if (StringUtils.isBlank(oleItemRecordForCirc.getItemType())) {
105 droolsResponse.addErrorMessage("Invalid item type for this item : " + oleItemRecordForCirc.getItemRecord().getBarCode());
106 return false;
107 }
108 if (null == oleItemRecordForCirc.getItemStatusRecord() || StringUtils.isBlank(oleItemRecordForCirc.getItemStatusRecord().getCode())) {
109 droolsResponse.addErrorMessage("Invalid item status for this item : " + oleItemRecordForCirc.getItemRecord().getBarCode());
110 return false;
111 }
112 return true;
113 }
114
115 private Map<OlePatronDocument, OlePatronDocument> identifyPatron(DroolsExchange droolsExchange) {
116 Map<OlePatronDocument, OlePatronDocument> patronForWhomLoanIsBeingProcessed = new HashMap<>();
117 OlePatronDocument currentBorrower = getCurrentBorrower(droolsExchange);
118 if (CollectionUtils.isNotEmpty(currentBorrower.getOleProxyPatronDocumentList())) {
119 if (currentBorrower.isCheckoutForSelf()) {
120 patronForWhomLoanIsBeingProcessed.put(currentBorrower, null);
121 } else {
122 List<OleProxyPatronDocument> oleProxyPatronDocumentList = currentBorrower.getOleProxyPatronDocumentList();
123 for (Iterator<OleProxyPatronDocument> iterator = oleProxyPatronDocumentList.iterator(); iterator.hasNext(); ) {
124 OleProxyPatronDocument proxyForPatron = iterator.next();
125 OlePatronDocument proxyForPatronDocument = proxyForPatron.getOlePatronDocument();
126 if (proxyForPatronDocument.isCheckoutForSelf()) {
127 patronForWhomLoanIsBeingProcessed.put(proxyForPatronDocument, currentBorrower);
128 }
129 }
130 }
131 } else {
132 patronForWhomLoanIsBeingProcessed.put(currentBorrower, null);
133 }
134 return patronForWhomLoanIsBeingProcessed;
135 }
136
137 private void handleNoticeTablePopulation(ItemRecord itemRecord) {
138 List<OLEDeliverNotice> deliverNotices = processNotices(noticeInfo, currentLoanDocument, itemRecord);
139
140
141 currentLoanDocument.setDeliverNotices(deliverNotices);
142 }
143
144 private void setPatronInfoForLoanDocument(Map<OlePatronDocument, OlePatronDocument> patronForWhomLoanIsBeingProcessed,
145 OleLoanDocument oleLoanDocument) {
146 OlePatronDocument olePatronDocument = patronForWhomLoanIsBeingProcessed.keySet().iterator().next();
147 OlePatronDocument proxyPatron = patronForWhomLoanIsBeingProcessed.get(olePatronDocument);
148
149 oleLoanDocument.setPatronId(olePatronDocument.getOlePatronId());
150 oleLoanDocument.setRealPatronBarcode(olePatronDocument.getBarcode());
151 if (null != proxyPatron) {
152 oleLoanDocument.setProxyPatronId(proxyPatron.getOlePatronId());
153 oleLoanDocument.setRealPatronName(proxyPatron.getPatronName());
154 }
155 }
156
157 private boolean isNotDamaged(ItemRecord itemRecord) {
158 Boolean damagedFlag = itemRecord.isItemDamagedStatus();
159 return !(damagedFlag == null ? false : damagedFlag.booleanValue());
160 }
161
162 private boolean isNotClaimsReturned(ItemRecord itemRecord) {
163 Boolean claimsReturnedFlag = itemRecord.getClaimsReturnedFlag();
164 return !(claimsReturnedFlag == null ? false : claimsReturnedFlag.booleanValue());
165 }
166
167 private void processDueDateBasedOnExpirationDate(OlePatronDocument olePatronDocument, OleLoanDocument
168 oleLoanDocument) {
169 if (olePatronDocument.getExpirationDate() != null && oleLoanDocument.getLoanDueDate() != null) {
170 Timestamp expirationDate = new Timestamp(olePatronDocument.getExpirationDate().getTime());
171 if (isPatronExpiringBeforeLoanDue(oleLoanDocument, expirationDate) && isPatronExpirationGreaterThanToday(expirationDate)) {
172 oleLoanDocument.setLoanDueDate(expirationDate);
173 }
174 }
175 }
176
177 private boolean isPatronExpirationGreaterThanToday(Timestamp expirationDate) {
178 return expirationDate.compareTo(new Date()) > 0;
179 }
180
181 private boolean isPatronExpiringBeforeLoanDue(OleLoanDocument oleLoanDocument, Timestamp expirationDate) {
182 return expirationDate.compareTo(oleLoanDocument.getLoanDueDate()) < 0;
183 }
184
185
186 public DroolsResponse proceedWithItemValidation(DroolsExchange droolsExchange) {
187 ItemRecord itemRecord = getItemRecord(droolsExchange);
188
189 OleItemRecordForCirc oleItemRecordForCirc = ItemInfoUtil.getInstance().getOleItemRecordForCirc(itemRecord);
190
191 currentLoanDocument = getCurrentLoanDocument(itemRecord.getBarCode());
192 if (subsequentRequestExistsForItem(oleItemRecordForCirc)) {
193 currentLoanDocument.setRequestPatron(true);
194 }
195
196 if (StringUtils.isNotBlank(currentLoanDocument.getLoanId())) {
197 if (currentLoanDocument.getOlePatron().getBarcode().equalsIgnoreCase(getCurrentBorrower(droolsExchange).getBarcode())) {
198 DroolsResponse droolsResponse = new DroolsResponse();
199 droolsResponse.addErrorMessageCode(DroolsConstants.CHECKED_OUT_BY_SAME_PATRON);
200 droolsResponse.addErrorMessage("Item already checkedout by same patron!");
201 return droolsResponse;
202 } else if(!(currentLoanDocument.getOlePatron().getBarcode().equalsIgnoreCase(getCurrentBorrower(droolsExchange).getBarcode()))){
203 LOG.error("Item currently loaned by some on else.");
204 DroolsResponse droolsResponse = new DroolsResponse();
205 droolsResponse.addErrorMessage("Item currently loaned by some on else.");
206 droolsResponse.addErrorMessageCode(DroolsConstants.GENERAL_MESSAGE_FLAG);
207 return droolsResponse;
208 }else if (null != oleItemRecordForCirc.getOleDeliverRequestBo() && !oleItemRecordForCirc.getOleDeliverRequestBo().getOlePatron().getOlePatronId().equals(getCurrentBorrower(droolsExchange).getOlePatronId())) {
209 DroolsResponse droolsResponse = new DroolsResponse();
210 droolsResponse.addErrorMessage("The current borrower is not the patron who had placed the request.");
211 droolsResponse.addOverridePermissions(DroolsConstants.GENERAL_BLOCK_PERMISSION);
212 droolsResponse.addErrorMessageCode(DroolsConstants.REQUEST_EXITS);
213 return droolsResponse;
214 }
215 }
216 noticeInfo = new NoticeInfo();
217
218 currentLoanDocument.setOleCirculationDesk(getSelectedCirculationDesk(droolsExchange));
219
220 List<Object> facts = new ArrayList<>();
221
222 facts.add(oleItemRecordForCirc);
223
224 DroolsResponse droolsResponse = new DroolsResponse();
225 facts.add(droolsResponse);
226
227 facts.add(currentLoanDocument);
228
229 Map<OlePatronDocument, OlePatronDocument> patronForWhomLoanIsBeingProcessed = identifyPatron(droolsExchange);
230
231 facts.add(getPatronDocument(patronForWhomLoanIsBeingProcessed));
232
233 facts.add(noticeInfo);
234
235 OleStopWatch oleStopWatch = new OleStopWatch();
236 oleStopWatch.start();
237 fireRules(facts, null, "checkout validation");
238 oleStopWatch.end();
239 LOG.info("Time taken to evaluvate rules for checkout item: " + (oleStopWatch.getTotalTime()) + " ms");
240
241 setItemValidationDone(true, droolsExchange);
242 setPatronInfoForLoanDocument(patronForWhomLoanIsBeingProcessed, currentLoanDocument);
243 currentLoanDocument.setCirculationLocationId(getCirculationLocationId(droolsExchange));
244 currentLoanDocument.setItemId(itemRecord.getBarCode());
245 currentLoanDocument.setItemUuid(itemRecord.getUniqueIdPrefix() + "-" + itemRecord.getItemId());
246 currentLoanDocument.setCreateDate(new Timestamp(System.currentTimeMillis()));
247 currentLoanDocument.setLoanOperatorId(getOperatorId(droolsExchange));
248
249 processDueDateBasedOnExpirationDate(getCurrentBorrower(droolsExchange), currentLoanDocument);
250
251 if (null == currentLoanDocument.getCirculationPolicyId()) {
252 droolsResponse.addErrorMessage("No Circulation Policy found that matches the patron/item combination. Please select a due date manually!");
253 droolsResponse.addErrorMessageCode(DroolsConstants.CUSTOM_DUE_DATE_REQUIRED_FLAG);
254 currentLoanDocument.setCirculationPolicyId("No Circ Policy Found");
255 noticeInfo.setNoticeType(DroolsConstants.REGULAR_LOANS_NOTICE_CONFIG);
256 return droolsResponse;
257 }
258
259 if (null != droolsResponse && StringUtils.isNotBlank(droolsResponse.retrieveErrorMessage())) {
260 return droolsResponse;
261 }
262
263 proceedToSaveLoan(droolsExchange);
264
265 return droolsResponse;
266 }
267
268 public OleLoanDocument getCurrentLoanDocument(String itemBarcode) {
269 HashMap<String, Object> parameterMap = new HashMap<>();
270 parameterMap.put("itemId", itemBarcode);
271 List<OleLoanDocument> matching = (List<OleLoanDocument>) getBusinessObjectService().findMatching(OleLoanDocument.class, parameterMap);
272 if (CollectionUtils.isNotEmpty(matching)) {
273 return matching.get(0);
274 }
275 return new OleLoanDocument();
276 }
277
278 private OlePatronDocument getPatronDocument(Map<OlePatronDocument, OlePatronDocument> patronForWhomLoanIsBeingProcessed) {
279 OlePatronDocument proxyPatronDocument = null;
280 for (Iterator<OlePatronDocument> iterator = patronForWhomLoanIsBeingProcessed.keySet().iterator(); iterator.hasNext(); ) {
281 OlePatronDocument olePatronDocument = iterator.next();
282 proxyPatronDocument = patronForWhomLoanIsBeingProcessed.get(olePatronDocument);
283 if (null == proxyPatronDocument) {
284 return olePatronDocument;
285 }
286 }
287 return patronForWhomLoanIsBeingProcessed.get(proxyPatronDocument);
288 }
289
290 public DroolsExchange proceedToSaveLoan(DroolsExchange droolsExchange) {
291 OleStopWatch oleStopWatch = new OleStopWatch();
292 oleStopWatch.start();
293 ItemRecord itemRecord = getItemRecord(droolsExchange);
294 addLoanDocumentToCurrentSession(currentLoanDocument, droolsExchange);
295
296 if (processCustomDueDateIfSet(droolsExchange, currentLoanDocument)) return droolsExchange;
297
298 if (null != currentLoanDocument.getLoanDueDate()) {
299 setDueDateTimeForItemRecord(droolsExchange, currentLoanDocument.getLoanDueDate());
300 handleNoticeTablePopulation(itemRecord);
301 }
302
303
304 OleLoanDocument savedLoanDocument = getBusinessObjectService().save(currentLoanDocument);
305 if (null != savedLoanDocument.getLoanId()) {
306 Boolean solrUpdateResults = updateItemInfoInSolr(getUpdateParameters(currentLoanDocument), itemRecord.getItemId(), true);
307 if (solrUpdateResults) {
308 updateLoanDocumentWithItemInformation(itemRecord, currentLoanDocument);
309 addCurrentLoandDocumentToListofLoandedToPatron(droolsExchange, currentLoanDocument);
310 LOG.info("Saved Loan with ID: " + savedLoanDocument.getLoanId());
311 cancelExistingRequestsIfExists(itemRecord.getBarCode());
312 } else {
313 rollBackSavedLoanRecord(itemRecord.getBarCode());
314 removeCurrentLoanDocumentFromCurrentSession(droolsExchange, currentLoanDocument);
315 }
316 } else {
317 clearCurrentSessionList(droolsExchange);
318 }
319
320 oleStopWatch.end();
321 LOG.info("Time taken to process the loan: " + (oleStopWatch.getTotalTime()) + " ms");
322 return droolsExchange;
323 }
324
325 public void cancelExistingRequestsIfExists(String itemBarcode) {
326 OleDeliverRequestBo oleDeliverRequestBo = ItemInfoUtil.getInstance().getPrioritizedRequest(itemBarcode);
327 if (null != oleDeliverRequestBo && (oleDeliverRequestBo.getBorrowerBarcode().equalsIgnoreCase(currentLoanDocument.getOlePatron().getBarcode()))) {
328 new OleDeliverRequestDocumentHelperServiceImpl().cancelDocument(oleDeliverRequestBo);
329 }
330 }
331
332 private Map getUpdateParameters(OleLoanDocument currentLoanDocument) {
333 HashMap parameterValues = new HashMap();
334 parameterValues.put("patronId", currentLoanDocument.getPatronId());
335 parameterValues.put("proxyPatronId", currentLoanDocument.getProxyPatronId());
336 parameterValues.put("itemCheckoutDateTime", currentLoanDocument.getCreateDate());
337 parameterValues.put("loanDueDate", currentLoanDocument.getLoanDueDate());
338 parameterValues.put("numRenewals", currentLoanDocument.getNumberOfRenewals());
339 parameterValues.put("itemStatus", OLEConstants.ITEM_STATUS_CHECKEDOUT);
340 return parameterValues;
341 }
342
343 private boolean subsequentRequestExistsForItem(OleItemRecordForCirc oleItemRecordForCirc) {
344 OleDeliverRequestBo oleDeliverRequestBo = oleItemRecordForCirc.getOleDeliverRequestBo();
345 if (null != oleDeliverRequestBo) {
346 if (oleDeliverRequestBo.getOleDeliverRequestType().getRequestTypeCode().contains(OLEConstants.OleDeliverRequest.RECALL)) {
347 HashMap<String, Object> map = new HashMap<>();
348 map.put("itemId", oleItemRecordForCirc.getItemRecord().getBarCode());
349 List<OleDeliverRequestBo> matching = (List<OleDeliverRequestBo>) getBusinessObjectService().findMatching(OleDeliverRequestBo.class, map);
350 if (CollectionUtils.isNotEmpty(matching)) {
351 if (matching.size() > 1) {
352 return true;
353 }
354 }
355 }
356 }
357 return false;
358 }
359 }