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.form.CircForm;
10 import org.kuali.ole.deliver.util.ErrorMessage;
11 import org.kuali.ole.deliver.util.ItemInfoUtil;
12 import org.kuali.ole.deliver.util.NoticeInfo;
13 import org.kuali.ole.deliver.util.OleItemRecordForCirc;
14 import org.kuali.ole.docstore.engine.service.storage.rdbms.pojo.ItemRecord;
15 import org.kuali.ole.utility.OleStopWatch;
16 import org.kuali.rice.krad.util.GlobalVariables;
17 import org.kuali.rice.krad.web.form.UifFormBase;
18
19 import java.sql.Timestamp;
20 import java.text.SimpleDateFormat;
21 import java.util.*;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25
26
27
28 public class CheckoutController extends CircUtilController {
29 private static final Logger LOG = Logger.getLogger(CheckoutController.class);
30 private OleLoanDocument currentLoanDocument;
31 private NoticeInfo noticeInfo;
32
33
34 public ErrorMessage lookupItemAndSaveLoan(UifFormBase form) {
35 CircForm circForm = (CircForm) form;
36 ErrorMessage errorMessage = null;
37
38 String itemBarcode = circForm.getItemBarcode();
39
40 ItemRecord itemRecord = getItemRecordByBarcode(itemBarcode);
41
42 if (null != itemRecord) {
43 circForm.setItemRecord(itemRecord);
44
45 if (isNotClaimsReturned(itemRecord) && (isAvailable(itemRecord) || isRecentlyReturned(itemRecord ))) {
46 currentLoanDocument = new OleLoanDocument();
47 noticeInfo = new NoticeInfo();
48
49 OleCirculationDesk oleCirculationDesk = getCircDeskLocationResolver().getCircDeskForOpertorId
50 (GlobalVariables.getUserSession().getPrincipalId());
51 currentLoanDocument.setOleCirculationDesk(oleCirculationDesk);
52
53 List<Object> facts = new ArrayList<>();
54 OleItemRecordForCirc oleItemRecordForCirc = ItemInfoUtil.getInstance().getOleItemRecordForCirc(itemRecord);
55 facts.add(oleItemRecordForCirc);
56
57 errorMessage = new ErrorMessage();
58 facts.add(errorMessage);
59
60 facts.add(currentLoanDocument);
61
62 Map<OlePatronDocument, OlePatronDocument> patronForWhomLoanIsBeingProcessed = identifyPatron(circForm);
63
64 facts.add(circForm.getPatronDocument().getSelectedProxyForPatron() == null ? circForm.getPatronDocument() : circForm.getPatronDocument().getSelectedProxyForPatron());
65
66 facts.add(noticeInfo);
67
68 OleStopWatch oleStopWatch = new OleStopWatch();
69 oleStopWatch.start();
70 fireRulesForPatron(facts, null, "checkout validation");
71 oleStopWatch.end();
72 LOG.info("Time taken to evaluvate rules for checkout item: " + (oleStopWatch.getTotalTime()) + " ms");
73
74 circForm.setItemValidationDone(true);
75 setPatronInfForLoanDocument(patronForWhomLoanIsBeingProcessed, currentLoanDocument);
76 currentLoanDocument.setCirculationLocationId(circForm.getSelectedCirculationDesk());
77 currentLoanDocument.setItemId(itemRecord.getBarCode());
78 currentLoanDocument.setItemUuid(itemRecord.getUniqueIdPrefix() + "-" + itemRecord.getItemId());
79 currentLoanDocument.setCreateDate(new Date(System.currentTimeMillis()));
80 currentLoanDocument.setLoanOperatorId(getLoginUserId());
81
82 processDueDateBasedOnExpirationDate(circForm.getPatronDocument(), currentLoanDocument);
83
84 if (null == currentLoanDocument.getCirculationPolicyId()) {
85 errorMessage.setErrorMessage("No Circulation Policy found that matches the patron/item combination. Please select a due date manually!");
86 errorMessage.setErrorCode(DroolsConstants.CUSTOM_DUE_DATE_REQUIRED_FLAG);
87 currentLoanDocument.setCirculationPolicyId("No Circ Policy Found");
88 noticeInfo.setLoanType(DroolsConstants.REGULAR_LOANS_NOTICE_CONFIG);
89 return errorMessage;
90 }
91
92 if(null!= errorMessage && StringUtils.isNotBlank(errorMessage.getErrorMessage())){
93 return errorMessage;
94 }
95
96 proceedToSaveLoan(circForm);
97
98 } else {
99 errorMessage = new ErrorMessage();
100 errorMessage.setErrorMessage("Only Item(s) with \"Available\" status can be checked out using this screen at this time!");
101 errorMessage.setErrorCode(DroolsConstants.GENERAL_MESSAGE_FLAG);
102 }
103 } else {
104 errorMessage = new ErrorMessage();
105 errorMessage.setErrorMessage("Invalid item barcode : " + itemBarcode);
106 errorMessage.setErrorCode(DroolsConstants.GENERAL_MESSAGE_FLAG);
107 }
108 return errorMessage;
109 }
110
111 private boolean isRecentlyReturned(ItemRecord itemRecord) {
112 return itemRecord.getItemStatusRecord().getCode().equalsIgnoreCase("RECENTLY-RETURNED");
113 }
114
115 private boolean isAvailable(ItemRecord itemRecord) {
116 return itemRecord.getItemStatusRecord().getCode().equalsIgnoreCase(OLEConstants.AVAILABLE);
117 }
118
119 private Map<OlePatronDocument, OlePatronDocument> identifyPatron(CircForm circForm) {
120 Map<OlePatronDocument, OlePatronDocument> patronForWhomLoanIsBeingProcessed = new HashMap<>();
121 OlePatronDocument currentBorrower = circForm.getPatronDocument();
122 if (CollectionUtils.isNotEmpty(currentBorrower.getOleProxyPatronDocumentList())) {
123 if (currentBorrower.isCheckoutForSelf()) {
124 patronForWhomLoanIsBeingProcessed.put(currentBorrower, null);
125 } else {
126 List<OleProxyPatronDocument> oleProxyPatronDocumentList = currentBorrower.getOleProxyPatronDocumentList();
127 for (Iterator<OleProxyPatronDocument> iterator = oleProxyPatronDocumentList.iterator(); iterator.hasNext(); ) {
128 OleProxyPatronDocument proxyForPatron = iterator.next();
129 OlePatronDocument proxyForPatronDocument = proxyForPatron.getOlePatronDocument();
130 if (proxyForPatronDocument.isCheckoutForSelf()) {
131 patronForWhomLoanIsBeingProcessed.put(proxyForPatronDocument, currentBorrower);
132 }
133 }
134 }
135 } else {
136 patronForWhomLoanIsBeingProcessed.put(currentBorrower, null);
137 }
138 return patronForWhomLoanIsBeingProcessed;
139 }
140
141 private void handleNoticeTablePopulation(ItemRecord itemRecord) {
142 List<Object> facts = new ArrayList<Object>();
143 facts.add(noticeInfo);
144 facts.add(itemRecord);
145
146 fireRulesForPatron(facts, null, "notice generation");
147
148 List<OLEDeliverNotice> deliverNotices = processNotices(noticeInfo, currentLoanDocument);
149
150
151 currentLoanDocument.setDeliverNotices(deliverNotices);
152 }
153
154
155 private void setPatronInfForLoanDocument(Map<OlePatronDocument, OlePatronDocument> patronForWhomLoanIsBeingProcessed,
156 OleLoanDocument oleLoanDocument) {
157 OlePatronDocument olePatronDocument = patronForWhomLoanIsBeingProcessed.keySet().iterator().next();
158 OlePatronDocument proxyPatron = patronForWhomLoanIsBeingProcessed.get(olePatronDocument);
159
160 oleLoanDocument.setPatronId(olePatronDocument.getOlePatronId());
161 oleLoanDocument.setRealPatronBarcode(olePatronDocument.getBarcode());
162 if (null != proxyPatron) {
163 oleLoanDocument.setProxyPatronId(proxyPatron.getOlePatronId());
164 oleLoanDocument.setRealPatronName(proxyPatron.getPatronName());
165 }
166 }
167
168 private boolean isNotClaimsReturned(ItemRecord itemRecord) {
169 Boolean claimsReturnedFlag = itemRecord.getClaimsReturnedFlag();
170 return !(claimsReturnedFlag == null ? false : claimsReturnedFlag.booleanValue());
171 }
172
173 private void processDueDateBasedOnExpirationDate(OlePatronDocument olePatronDocument, OleLoanDocument
174 oleLoanDocument) {
175 if (olePatronDocument.getExpirationDate() != null && oleLoanDocument.getLoanDueDate() != null) {
176 Timestamp expirationDate = new Timestamp(olePatronDocument.getExpirationDate().getTime());
177 if (isPatronExpiringBeforeLoanDue(oleLoanDocument, expirationDate) && isPatronExpirationGreaterThanToday(expirationDate)) {
178 oleLoanDocument.setLoanDueDate(expirationDate);
179 }
180 }
181 }
182
183 private boolean isPatronExpirationGreaterThanToday(Timestamp expirationDate) {
184 return expirationDate.compareTo(new Date()) > 0;
185 }
186
187 private boolean isPatronExpiringBeforeLoanDue(OleLoanDocument oleLoanDocument, Timestamp expirationDate) {
188 return expirationDate.compareTo(oleLoanDocument.getLoanDueDate()) < 0;
189 }
190
191 public UifFormBase proceedToSaveLoan(UifFormBase form) {
192 OleStopWatch oleStopWatch = new OleStopWatch();
193 oleStopWatch.start();
194 CircForm circForm = (CircForm) form;
195 ItemRecord itemRecord = circForm.getItemRecord();
196 circForm.getLoanDocumentListForCurrentSession().add(currentLoanDocument);
197
198 if (processCustomDueDateIfSet(circForm)) return circForm;
199
200 if (null != currentLoanDocument.getLoanDueDate()) {
201 circForm.getItemRecord().setDueDateTime(currentLoanDocument.getLoanDueDate());
202 handleNoticeTablePopulation(itemRecord);
203 }
204
205 OleLoanDocument savedLoanDocument = getBusinessObjectService().save(currentLoanDocument);
206 if (null != savedLoanDocument.getLoanId()) {
207 Boolean solrUpdateResults = updateItemInfoInSolr(currentLoanDocument, itemRecord.getItemId());
208 if (solrUpdateResults) {
209 updateLoanDocumentWithItemInformation(itemRecord, currentLoanDocument);
210 circForm.getPatronDocument().getOleLoanDocuments().add(currentLoanDocument);
211 LOG.info("Saved Loan with ID: " + savedLoanDocument.getLoanId());
212 } else {
213 rollBackSavedLoanRecord(itemRecord.getBarCode());
214 circForm.getLoanDocumentListForCurrentSession().remove(currentLoanDocument);
215 }
216 } else {
217 circForm.getLoanDocumentListForCurrentSession().clear();
218 }
219 oleStopWatch.end();
220 LOG.info("Time taken to process the loan: " + (oleStopWatch.getTotalTime()) + " ms" );
221 return circForm;
222 }
223
224 private boolean processCustomDueDateIfSet(CircForm circForm) {
225 if (null != circForm.getCustomDueDateMap()) {
226 try {
227 processCustomDueDate(circForm);
228 } catch (Exception e) {
229 return true;
230 }
231 }
232 return false;
233 }
234
235 private void processCustomDueDate(CircForm circForm) throws Exception {
236 boolean timeFlag = false;
237 Timestamp timestamp;
238 Pattern pattern;
239 Matcher matcher;
240 SimpleDateFormat fmt = new SimpleDateFormat(OLEConstants.OlePatron.PATRON_MAINTENANCE_DATE_FORMAT);
241
242 String customDueDateTime = circForm.getCustomDueDateTime();
243 Date customDueDateMap = circForm.getCustomDueDateMap();
244
245 if (StringUtils.isNotBlank(customDueDateTime)) {
246 String[] str = customDueDateTime.split(":");
247 pattern = Pattern.compile(OLEConstants.TIME_24_HR_PATTERN);
248 matcher = pattern.matcher(customDueDateTime);
249 timeFlag = matcher.matches();
250 if (timeFlag) {
251 if (str != null && str.length <= 2) {
252 circForm.setCustomDueDateTime(customDueDateTime + OLEConstants.CHECK_IN_TIME_MS);
253 }
254 timestamp = Timestamp.valueOf(new SimpleDateFormat(OLEConstants.CHECK_IN_DATE_TIME_FORMAT).format(customDueDateMap).concat(" ").concat(customDueDateTime));
255 } else {
256 circForm.setCustomDueDateTimeMessage(OLEConstants.DUE_DATE_TIME_FORMAT_MESSAGE);
257 throw new Exception();
258 }
259 } else if (fmt.format(customDueDateMap).compareTo(fmt.format(new Date())) == 0) {
260 timestamp = new Timestamp(new Date().getTime());
261 } else {
262 timestamp = Timestamp.valueOf(new SimpleDateFormat(OLEConstants.CHECK_IN_DATE_TIME_FORMAT).format(customDueDateMap).concat(" ").concat(new SimpleDateFormat("HH:mm:ss").format(new Date())));
263 }
264 currentLoanDocument.setLoanDueDate(timestamp);
265 currentLoanDocument.setCirculationPolicyId("No Circulation Policy Matched");
266 }
267 }