Fees and structures added
This commit is contained in:
@@ -75,6 +75,13 @@ public class WebhookController {
|
||||
String messageType = messageNode.get("type").asText();
|
||||
|
||||
UserState currentState = sessionService.getUserState(fromPhoneNumber);
|
||||
|
||||
boolean isInMemory = sessionService.isSessionCached(fromPhoneNumber);
|
||||
boolean hasDbRecord = sessionService.hasSavedCredentials(fromPhoneNumber);
|
||||
if (!isInMemory && hasDbRecord) {
|
||||
messageService.sendMessage(fromPhoneNumber, "Authenticating User with IMS... ⏳");
|
||||
}
|
||||
|
||||
boolean isAuthenticated = sessionService.isAuthenticated(fromPhoneNumber);
|
||||
|
||||
// Global reset command
|
||||
@@ -166,7 +173,7 @@ public class WebhookController {
|
||||
messageService.sendMessage(phoneNumber, "This menu selection is no longer active.");
|
||||
studentMenuHandler.sendMainMenu(phoneNumber);
|
||||
}
|
||||
} else if (selectedListId.startsWith("receipt_")) {
|
||||
} else if (selectedListId.startsWith("receipt_") || selectedListId.startsWith("exam_") || selectedListId.startsWith("acad_") || selectedListId.startsWith("cons_")) {
|
||||
// They clicked a receipt download option
|
||||
if (currentState == UserState.AWAITING_RECEIPT_SELECTION) {
|
||||
studentMenuHandler.handleReceiptSelection(phoneNumber, selectedListId);
|
||||
@@ -191,6 +198,16 @@ public class WebhookController {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("student_exam_fees".equals(selectedButtonId)) {
|
||||
studentMenuHandler.handleExamFees(phoneNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("student_payment_history".equals(selectedButtonId)) {
|
||||
studentMenuHandler.handlePaymentHistory(phoneNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentState == UserState.AWAITING_GRIEVANCE_CATEGORY) {
|
||||
grievanceHandler.handleCategorySelection(phoneNumber, selectedButtonId);
|
||||
} else {
|
||||
|
||||
@@ -97,30 +97,29 @@ public class StudentMenuHandler {
|
||||
sendSemesterSelection(phoneNumber, token);
|
||||
break;
|
||||
|
||||
case "student_fee_details":
|
||||
case "student_fee_details": {
|
||||
String rawFees = imsApiService.getFees(token);
|
||||
String rawExamFees = imsApiService.getExamFees(token);
|
||||
String bodyText = MessageFormatter.formatFeesAndExamFees(rawFees, rawExamFees);
|
||||
String bodyText = MessageFormatter.formatAcademicFeesOnly(rawFees);
|
||||
|
||||
List<Map<String, String>> receiptRows = MessageFormatter.extractReceiptRows(rawExamFees);
|
||||
List<Map<String, String>> feeButtons = new ArrayList<>();
|
||||
Map<String, String> examBtn = new HashMap<>();
|
||||
examBtn.put("id", "student_exam_fees");
|
||||
examBtn.put("title", "Exam Fees");
|
||||
feeButtons.add(examBtn);
|
||||
|
||||
if (!receiptRows.isEmpty()) {
|
||||
// Max 10 items in a list. Limit to 10 if necessary.
|
||||
if (receiptRows.size() > 10) receiptRows = receiptRows.subList(0, 10);
|
||||
|
||||
sessionService.setUserState(phoneNumber, UserState.AWAITING_RECEIPT_SELECTION);
|
||||
|
||||
// Meta API interactive list body limit is 1024 characters
|
||||
if (bodyText.length() > 1024) bodyText = bodyText.substring(0, 1021) + "...";
|
||||
|
||||
Map<String, Object> listMessage = WhatsAppMessageBuilder.buildListMessage(
|
||||
"Receipts", bodyText, "Download Receipt", receiptRows
|
||||
);
|
||||
messageService.sendInteractiveMessage(phoneNumber, listMessage);
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, bodyText);
|
||||
Map<String, String> historyBtn = new HashMap<>();
|
||||
historyBtn.put("id", "student_payment_history");
|
||||
historyBtn.put("title", "Payment History");
|
||||
feeButtons.add(historyBtn);
|
||||
|
||||
if (bodyText.length() > 1020) {
|
||||
bodyText = bodyText.substring(0, 1017) + "...";
|
||||
}
|
||||
|
||||
Map<String, Object> feeButtonMessage = WhatsAppMessageBuilder.buildButtonMessage(bodyText, feeButtons);
|
||||
messageService.sendInteractiveMessage(phoneNumber, feeButtonMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
case "student_raise_grievance":
|
||||
grievanceHandler.startGrievanceFlow(phoneNumber);
|
||||
@@ -209,15 +208,31 @@ public class StudentMenuHandler {
|
||||
}
|
||||
|
||||
try {
|
||||
String historyId = selectedOptionId.replace("receipt_", "");
|
||||
byte[] pdfBytes = imsApiService.getExamFeeReceiptPdf(token, historyId);
|
||||
byte[] pdfBytes = null;
|
||||
String fileName = "Receipt.pdf";
|
||||
String docMessage = "Here is your Fee Receipt!";
|
||||
|
||||
if (selectedOptionId.startsWith("exam_")) {
|
||||
String longId = sessionService.getReceiptIdMapping(phoneNumber, selectedOptionId);
|
||||
pdfBytes = imsApiService.getExamFeeReceiptPdf(token, longId);
|
||||
fileName = "Exam_Receipt.pdf";
|
||||
docMessage = "Here is your Exam Fee Receipt!";
|
||||
} else if (selectedOptionId.startsWith("acad_")) {
|
||||
String longId = sessionService.getReceiptIdMapping(phoneNumber, selectedOptionId);
|
||||
pdfBytes = imsApiService.getAcademicFeeReceiptPdf(token, longId);
|
||||
fileName = "Academic_Receipt.pdf";
|
||||
docMessage = "Here is your Academic Fee Receipt!";
|
||||
} else if (selectedOptionId.startsWith("cons_")) {
|
||||
String longId = sessionService.getReceiptIdMapping(phoneNumber, selectedOptionId);
|
||||
pdfBytes = imsApiService.getConsolidatedAcademicFeeReceiptPdf(token, longId);
|
||||
fileName = "Consolidated_Receipt.pdf";
|
||||
docMessage = "Here is your Consolidated Academic Fee Receipt!";
|
||||
}
|
||||
|
||||
if (pdfBytes != null) {
|
||||
String fileName = "Receipt_" + historyId + ".pdf";
|
||||
String mediaId = messageService.uploadMedia(pdfBytes, fileName, "application/pdf");
|
||||
|
||||
if (mediaId != null) {
|
||||
messageService.sendDocumentMessage(phoneNumber, mediaId, "Here is your Exam Fee Receipt!", fileName);
|
||||
messageService.sendDocumentMessage(phoneNumber, mediaId, docMessage, fileName);
|
||||
sendResponseWithMenu(phoneNumber, "Select an option from the menu below.");
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, "Failed to upload the receipt document to WhatsApp.");
|
||||
@@ -249,6 +264,59 @@ public class StudentMenuHandler {
|
||||
sendResponseWithMenu(phoneNumber, "Select an option from the menu below.");
|
||||
}
|
||||
|
||||
public void handleExamFees(String phoneNumber) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
if (token == null) {
|
||||
messageService.sendMessage(phoneNumber, "Your session has expired. Please type 'Reset' to login again.");
|
||||
return;
|
||||
}
|
||||
|
||||
String rawExamFees = imsApiService.getExamFees(token);
|
||||
String formattedExamFees = MessageFormatter.formatExamFees(rawExamFees);
|
||||
sendResponseWithMenu(phoneNumber, formattedExamFees);
|
||||
}
|
||||
|
||||
public void handlePaymentHistory(String phoneNumber) {
|
||||
String token = sessionService.getImsToken(phoneNumber);
|
||||
if (token == null) {
|
||||
messageService.sendMessage(phoneNumber, "Your session has expired. Please type 'Reset' to login again.");
|
||||
return;
|
||||
}
|
||||
|
||||
String rawExamFees = imsApiService.getExamFees(token);
|
||||
String rawFeesHistory = imsApiService.getAcademicFeesHistory(token);
|
||||
|
||||
String formattedHistory = MessageFormatter.formatCombinedPaymentHistory(rawExamFees, rawFeesHistory);
|
||||
|
||||
Map<String, String> idMappings = new HashMap<>();
|
||||
List<Map<String, String>> examRows = MessageFormatter.extractReceiptRows(rawExamFees, idMappings);
|
||||
List<Map<String, String>> acadRows = MessageFormatter.extractAcademicReceiptRows(rawFeesHistory, idMappings);
|
||||
List<Map<String, String>> consRows = MessageFormatter.extractConsolidatedReceiptRows(rawFeesHistory, idMappings);
|
||||
|
||||
sessionService.storeReceiptIdMappings(phoneNumber, idMappings);
|
||||
|
||||
List<Map<String, String>> receiptRows = new ArrayList<>();
|
||||
receiptRows.addAll(examRows);
|
||||
receiptRows.addAll(acadRows);
|
||||
receiptRows.addAll(consRows);
|
||||
|
||||
if (!receiptRows.isEmpty()) {
|
||||
if (receiptRows.size() > 10) receiptRows = receiptRows.subList(0, 10);
|
||||
sessionService.setUserState(phoneNumber, UserState.AWAITING_RECEIPT_SELECTION);
|
||||
|
||||
if (formattedHistory.length() > 1024) {
|
||||
formattedHistory = formattedHistory.substring(0, 1021) + "...";
|
||||
}
|
||||
|
||||
Map<String, Object> listMessage = WhatsAppMessageBuilder.buildListMessage(
|
||||
"Receipts", formattedHistory, "Download Receipt", receiptRows
|
||||
);
|
||||
messageService.sendInteractiveMessage(phoneNumber, listMessage);
|
||||
} else {
|
||||
sendResponseWithMenu(phoneNumber, formattedHistory);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendResponseWithMenu(String phoneNumber, String bodyText) {
|
||||
// Meta API interactive list body limit is 1024 characters
|
||||
if (bodyText.length() > 1024) {
|
||||
|
||||
@@ -134,7 +134,15 @@ public class ImsApiService {
|
||||
}
|
||||
|
||||
public byte[] getExamFeeReceiptPdf(String token, String historyId) {
|
||||
String url = BASE_URL + "/api/student/exam-fee/receipt?history_id=" + historyId;
|
||||
String cleanId = historyId;
|
||||
if (cleanId.startsWith("?history_id=")) {
|
||||
cleanId = cleanId.substring(12);
|
||||
} else if (cleanId.startsWith("?id=")) {
|
||||
cleanId = cleanId.substring(4);
|
||||
} else if (cleanId.startsWith("?")) {
|
||||
cleanId = cleanId.substring(1);
|
||||
}
|
||||
String url = BASE_URL + "/api/student/exam-fee/receipt?history_id=" + cleanId;
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(token);
|
||||
|
||||
@@ -186,4 +194,60 @@ public class ImsApiService {
|
||||
}
|
||||
return "Student";
|
||||
}
|
||||
|
||||
public String getAcademicFeesHistory(String token) {
|
||||
return fetchFromIms("/api/student/fees/history", token);
|
||||
}
|
||||
|
||||
public byte[] getAcademicFeeReceiptPdf(String token, String historyId) {
|
||||
String cleanId = historyId;
|
||||
if (cleanId.startsWith("?history_id=")) {
|
||||
cleanId = cleanId.substring(12);
|
||||
} else if (cleanId.startsWith("?id=")) {
|
||||
cleanId = cleanId.substring(4);
|
||||
} else if (cleanId.startsWith("?")) {
|
||||
cleanId = cleanId.substring(1);
|
||||
}
|
||||
String url = BASE_URL + "/api/student/fees/download-receipt?history_id=" + cleanId;
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(token);
|
||||
|
||||
HttpEntity<String> request = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, request, byte[].class);
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
return response.getBody();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to download Academic Fee receipt PDF: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] getConsolidatedAcademicFeeReceiptPdf(String token, String id) {
|
||||
String cleanId = id;
|
||||
if (cleanId.startsWith("?id=")) {
|
||||
cleanId = cleanId.substring(4);
|
||||
} else if (cleanId.startsWith("?history_id=")) {
|
||||
cleanId = cleanId.substring(12);
|
||||
} else if (cleanId.startsWith("?")) {
|
||||
cleanId = cleanId.substring(1);
|
||||
}
|
||||
String url = BASE_URL + "/api/student/fees/download-consolidated-receipt?id=" + cleanId;
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(token);
|
||||
|
||||
HttpEntity<String> request = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(url, org.springframework.http.HttpMethod.GET, request, byte[].class);
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
return response.getBody();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to download Consolidated Academic Fee receipt PDF: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,14 @@ public class SessionService {
|
||||
userStates.put(phoneNumber, state);
|
||||
}
|
||||
|
||||
public boolean isSessionCached(String phoneNumber) {
|
||||
return sessionTokens.containsKey(phoneNumber);
|
||||
}
|
||||
|
||||
public boolean hasSavedCredentials(String phoneNumber) {
|
||||
return userRepository.existsById(phoneNumber);
|
||||
}
|
||||
|
||||
public void resetState(String phoneNumber) {
|
||||
userStates.remove(phoneNumber);
|
||||
}
|
||||
@@ -104,6 +112,20 @@ public class SessionService {
|
||||
return name;
|
||||
}
|
||||
|
||||
private final Map<String, Map<String, String>> receiptIdMappings = new ConcurrentHashMap<>();
|
||||
|
||||
public void storeReceiptIdMappings(String phoneNumber, Map<String, String> mappings) {
|
||||
receiptIdMappings.put(phoneNumber, mappings);
|
||||
}
|
||||
|
||||
public String getReceiptIdMapping(String phoneNumber, String shortId) {
|
||||
Map<String, String> mappings = receiptIdMappings.get(phoneNumber);
|
||||
if (mappings != null) {
|
||||
return mappings.get(shortId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setTempLoginId(String phoneNumber, String tempId) {
|
||||
temporaryLoginIds.put(phoneNumber, tempId);
|
||||
}
|
||||
|
||||
@@ -51,10 +51,9 @@ public class MessageFormatter {
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW ATTENDANCE JSON:*\n").append(rawAttendance).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting attendance data.\nRAW JSON: ").append(rawAttendance).append("\n");
|
||||
sb.append("⚠️ Error formatting attendance data.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +84,9 @@ public class MessageFormatter {
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
sb.append("\n*DEBUG RAW LEAVES JSON:*\n").append(rawLeaves).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting leaves data.\nRAW JSON: ").append(rawLeaves).append("\n");
|
||||
sb.append("⚠️ Error formatting leaves data.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,23 +480,159 @@ public class MessageFormatter {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static java.util.List<java.util.Map<String, String>> extractReceiptRows(String rawExamFees) {
|
||||
public static String formatAcademicFeesOnly(String rawFees) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("💵 *Academic Dues / Fees*\n\n");
|
||||
if (rawFees == null) {
|
||||
sb.append("❌ Failed to retrieve academic fee details.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawFees);
|
||||
boolean foundData = false;
|
||||
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
if (data.isArray() && data.size() > 0) {
|
||||
for (JsonNode fee : data) {
|
||||
String type = fee.has("type") ? fee.get("type").asText() :
|
||||
(fee.has("description") ? fee.get("description").asText() : "Fee");
|
||||
String amount = fee.has("amount") ? fee.get("amount").asText() : "0";
|
||||
String status = fee.has("status") ? fee.get("status").asText() : "N/A";
|
||||
|
||||
sb.append("- *").append(type).append("*: ₹").append(amount).append(" (").append(status).append(")\n");
|
||||
foundData = true;
|
||||
}
|
||||
} else if (data.isObject()) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = data.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
sb.append("- *").append(field.getKey().replace("_", " ")).append("*: ").append(field.getValue().asText()).append("\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("Details not available in this format.\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting academic fee data.\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String formatExamFees(String rawExamFees) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("📝 *Exam Fees*\n\n");
|
||||
if (rawExamFees == null) {
|
||||
sb.append("❌ Failed to retrieve exam fee details.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawExamFees);
|
||||
boolean foundData = false;
|
||||
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
if (data.isArray() && data.size() > 0) {
|
||||
for (JsonNode fee : data) {
|
||||
String status = fee.has("status") ? fee.get("status").asText() : "";
|
||||
if (!status.equalsIgnoreCase("Paid") && !status.equalsIgnoreCase("Success")) {
|
||||
String semester = fee.has("semester") ? fee.get("semester").asText() : "Unknown Sem";
|
||||
String amount = fee.has("amount") ? fee.get("amount").asText() : "0";
|
||||
String displayStatus = status.isEmpty() ? "Unpaid" : status;
|
||||
sb.append("- *Sem ").append(semester).append("*: ₹").append(amount).append(" (").append(displayStatus).append(")\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("No outstanding exam fees found.\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting exam fee data.\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String formatPaymentHistory(String rawExamFees) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("📜 *Payment History*\n\n");
|
||||
if (rawExamFees == null) {
|
||||
sb.append("❌ Failed to retrieve payment history.\n");
|
||||
} else {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawExamFees);
|
||||
boolean foundData = false;
|
||||
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
if (data.isArray() && data.size() > 0) {
|
||||
for (JsonNode fee : data) {
|
||||
String status = fee.has("status") ? fee.get("status").asText() : "";
|
||||
if (status.equalsIgnoreCase("Paid") || status.equalsIgnoreCase("Success") || fee.has("history_id")) {
|
||||
String semester = fee.has("semester") ? fee.get("semester").asText() : "Unknown Sem";
|
||||
String amount = fee.has("amount") ? fee.get("amount").asText() : "0";
|
||||
String date = fee.has("date") ? " on " + fee.get("date").asText() : "";
|
||||
sb.append("- *Sem ").append(semester).append("*: ₹").append(amount).append(" (Paid").append(date).append(")\n");
|
||||
foundData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundData) {
|
||||
sb.append("No payment history found.\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("⚠️ Error formatting payment history.\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static java.util.List<java.util.Map<String, String>> extractReceiptRows(String rawExamFees, java.util.Map<String, String> idMappings) {
|
||||
java.util.List<java.util.Map<String, String>> rows = new java.util.ArrayList<>();
|
||||
if (rawExamFees == null) return rows;
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawExamFees);
|
||||
if (root.has("data") && root.get("data").isArray()) {
|
||||
for (JsonNode fee : root.get("data")) {
|
||||
if (fee.has("history_id") || fee.has("id")) {
|
||||
String id = fee.has("history_id") ? fee.get("history_id").asText() : fee.get("id").asText();
|
||||
String amount = fee.has("amount") ? fee.get("amount").asText() : "";
|
||||
String date = fee.has("date") ? fee.get("date").asText() : "";
|
||||
|
||||
java.util.Map<String, String> row = new java.util.HashMap<>();
|
||||
row.put("id", "receipt_" + id);
|
||||
row.put("title", "Receipt " + id);
|
||||
row.put("description", "₹" + amount + (date.isEmpty() ? "" : " on " + date));
|
||||
rows.add(row);
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
JsonNode historyNode = null;
|
||||
if (data.isObject() && data.has("history")) {
|
||||
historyNode = data.get("history");
|
||||
}
|
||||
|
||||
if (historyNode != null && historyNode.isObject()) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = historyNode.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
JsonNode arrayNode = field.getValue();
|
||||
if (arrayNode.isArray()) {
|
||||
for (JsonNode fee : arrayNode) {
|
||||
if (fee.has("history_id") || fee.has("id")) {
|
||||
String id = fee.has("history_id") ? fee.get("history_id").asText() : fee.get("id").asText();
|
||||
String amount = fee.has("paid_amt") ? fee.get("paid_amt").asText() : (fee.has("amount") ? fee.get("amount").asText() : "");
|
||||
String date = fee.has("paid_date") ? fee.get("paid_date").asText() : (fee.has("date") ? fee.get("date").asText() : "");
|
||||
|
||||
java.util.Map<String, String> row = new java.util.HashMap<>();
|
||||
String shortId = "exam_" + (rows.size() + 1);
|
||||
idMappings.put(shortId, id);
|
||||
row.put("id", shortId);
|
||||
|
||||
String title = "Receipt " + date;
|
||||
if (title.length() > 24 || date.isEmpty()) {
|
||||
title = "Exam Receipt #" + (rows.size() + 1);
|
||||
}
|
||||
row.put("title", title);
|
||||
row.put("description", "₹" + amount + (date.isEmpty() ? "" : " on " + date));
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -506,6 +640,225 @@ public class MessageFormatter {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public static java.util.List<java.util.Map<String, String>> extractAcademicReceiptRows(String rawFeesHistory, java.util.Map<String, String> idMappings) {
|
||||
java.util.List<java.util.Map<String, String>> rows = new java.util.ArrayList<>();
|
||||
if (rawFeesHistory == null) return rows;
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawFeesHistory);
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
JsonNode historyNode = null;
|
||||
|
||||
if (data.isObject()) {
|
||||
if (data.has("history")) historyNode = data.get("history");
|
||||
else if (data.has("payment_history")) historyNode = data.get("payment_history");
|
||||
else if (data.has("paymentHistory")) historyNode = data.get("paymentHistory");
|
||||
else if (data.has("logs")) historyNode = data.get("logs");
|
||||
}
|
||||
|
||||
if (historyNode != null) {
|
||||
if (historyNode.isObject()) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = historyNode.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
JsonNode arrayNode = field.getValue();
|
||||
if (arrayNode.isArray()) {
|
||||
for (JsonNode fee : arrayNode) {
|
||||
if (fee.has("history_id") || fee.has("id")) {
|
||||
String id = fee.has("history_id") ? fee.get("history_id").asText() : fee.get("id").asText();
|
||||
String amount = fee.has("paid_amt") ? fee.get("paid_amt").asText() : (fee.has("amount") ? fee.get("amount").asText() : "");
|
||||
String date = fee.has("paid_date") ? fee.get("paid_date").asText() : (fee.has("date") ? fee.get("date").asText() : "");
|
||||
String type = fee.has("type") ? fee.get("type").asText() :
|
||||
(fee.has("description") ? fee.get("description").asText() : (fee.has("name") ? fee.get("name").asText() : "Academic Fee"));
|
||||
|
||||
java.util.Map<String, String> row = new java.util.HashMap<>();
|
||||
String shortId = "acad_" + (rows.size() + 1);
|
||||
idMappings.put(shortId, id);
|
||||
row.put("id", shortId);
|
||||
|
||||
String title = "Receipt " + date;
|
||||
if (title.length() > 24 || date.isEmpty()) {
|
||||
title = "Acad Receipt #" + (rows.size() + 1);
|
||||
}
|
||||
row.put("title", title);
|
||||
row.put("description", type + ": ₹" + amount + (date.isEmpty() ? "" : " on " + date));
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (historyNode.isArray()) {
|
||||
for (JsonNode fee : historyNode) {
|
||||
if (fee.has("history_id") || fee.has("id")) {
|
||||
String id = fee.has("history_id") ? fee.get("history_id").asText() : fee.get("id").asText();
|
||||
String amount = fee.has("paid_amt") ? fee.get("paid_amt").asText() : (fee.has("amount") ? fee.get("amount").asText() : "");
|
||||
String date = fee.has("paid_date") ? fee.get("paid_date").asText() : (fee.has("date") ? fee.get("date").asText() : "");
|
||||
String type = fee.has("type") ? fee.get("type").asText() :
|
||||
(fee.has("description") ? fee.get("description").asText() : "Academic Fee");
|
||||
|
||||
java.util.Map<String, String> row = new java.util.HashMap<>();
|
||||
String shortId = "acad_" + (rows.size() + 1);
|
||||
idMappings.put(shortId, id);
|
||||
row.put("id", shortId);
|
||||
|
||||
String title = "Receipt " + date;
|
||||
if (title.length() > 24 || date.isEmpty()) {
|
||||
title = "Acad Receipt #" + (rows.size() + 1);
|
||||
}
|
||||
row.put("title", title);
|
||||
row.put("description", type + ": ₹" + amount + (date.isEmpty() ? "" : " on " + date));
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
return rows;
|
||||
}
|
||||
|
||||
public static java.util.List<java.util.Map<String, String>> extractConsolidatedReceiptRows(String rawFeesHistory, java.util.Map<String, String> idMappings) {
|
||||
java.util.List<java.util.Map<String, String>> rows = new java.util.ArrayList<>();
|
||||
if (rawFeesHistory == null) return rows;
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawFeesHistory);
|
||||
if (root.has("data") && root.get("data").isObject()) {
|
||||
JsonNode data = root.get("data");
|
||||
JsonNode consolidatedNode = null;
|
||||
|
||||
if (data.has("consolidated")) consolidatedNode = data.get("consolidated");
|
||||
else if (data.has("consolidated_receipts")) consolidatedNode = data.get("consolidated_receipts");
|
||||
else if (data.has("consolidatedReceipts")) consolidatedNode = data.get("consolidatedReceipts");
|
||||
|
||||
if (consolidatedNode != null && consolidatedNode.isArray()) {
|
||||
for (JsonNode rec : consolidatedNode) {
|
||||
if (rec.has("id")) {
|
||||
String id = rec.get("id").asText();
|
||||
String academicYear = rec.has("academic_year") ? rec.get("academic_year").asText() : "";
|
||||
String totalFee = rec.has("total_fee") ? rec.get("total_fee").asText() : "";
|
||||
String paidFee = rec.has("paid_fee") ? rec.get("paid_fee").asText() : "";
|
||||
|
||||
java.util.Map<String, String> row = new java.util.HashMap<>();
|
||||
String shortId = "cons_" + (rows.size() + 1);
|
||||
idMappings.put(shortId, id);
|
||||
row.put("id", shortId);
|
||||
|
||||
// WhatsApp interactive list row titles must be <= 24 characters
|
||||
String title = "Consolidated " + academicYear;
|
||||
if (title.length() > 24 || academicYear.isEmpty()) {
|
||||
title = "Consolidated #" + (rows.size() + 1);
|
||||
}
|
||||
row.put("title", title);
|
||||
|
||||
String description = "Paid: ₹" + paidFee + " / ₹" + totalFee;
|
||||
if (description.length() > 72) {
|
||||
description = description.substring(0, 69) + "...";
|
||||
}
|
||||
row.put("description", description);
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
return rows;
|
||||
}
|
||||
|
||||
public static String formatCombinedPaymentHistory(String rawExamFees, String rawFeesHistory) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// --- Parse Exam Fee History ---
|
||||
sb.append("📝 *Exam Fee Payment History*\n\n");
|
||||
boolean foundExam = false;
|
||||
if (rawExamFees != null) {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawExamFees);
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
JsonNode historyNode = null;
|
||||
if (data.isObject() && data.has("history")) {
|
||||
historyNode = data.get("history");
|
||||
}
|
||||
|
||||
if (historyNode != null && historyNode.isObject()) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = historyNode.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
JsonNode arrayNode = field.getValue();
|
||||
if (arrayNode.isArray()) {
|
||||
for (JsonNode fee : arrayNode) {
|
||||
String semester = fee.has("semester") ? fee.get("semester").asText() : "Unknown Sem";
|
||||
String amount = fee.has("paid_amt") ? fee.get("paid_amt").asText() : (fee.has("amount") ? fee.get("amount").asText() : "0");
|
||||
String date = fee.has("paid_date") ? " on " + fee.get("paid_date").asText() : (fee.has("date") ? " on " + fee.get("date").asText() : "");
|
||||
sb.append("- *Sem ").append(semester).append("*: ₹").append(amount).append(" (Paid").append(date).append(")\n");
|
||||
foundExam = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
if (!foundExam) {
|
||||
sb.append("No exam fee payment history found.\n");
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
|
||||
// --- Parse Academic Fee History ---
|
||||
sb.append("💵 *Academic Fee Payment History*\n\n");
|
||||
boolean foundAcad = false;
|
||||
if (rawFeesHistory != null) {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(rawFeesHistory);
|
||||
if (root.has("data")) {
|
||||
JsonNode data = root.get("data");
|
||||
JsonNode historyNode = null;
|
||||
if (data.isObject()) {
|
||||
if (data.has("history")) historyNode = data.get("history");
|
||||
else if (data.has("payment_history")) historyNode = data.get("payment_history");
|
||||
else if (data.has("paymentHistory")) historyNode = data.get("paymentHistory");
|
||||
else if (data.has("logs")) historyNode = data.get("logs");
|
||||
}
|
||||
|
||||
if (historyNode != null) {
|
||||
if (historyNode.isObject()) {
|
||||
java.util.Iterator<java.util.Map.Entry<String, JsonNode>> fields = historyNode.fields();
|
||||
while (fields.hasNext()) {
|
||||
java.util.Map.Entry<String, JsonNode> field = fields.next();
|
||||
JsonNode arrayNode = field.getValue();
|
||||
if (arrayNode.isArray()) {
|
||||
for (JsonNode fee : arrayNode) {
|
||||
String amount = fee.has("paid_amt") ? fee.get("paid_amt").asText() : (fee.has("amount") ? fee.get("amount").asText() : "");
|
||||
String date = fee.has("paid_date") ? " on " + fee.get("paid_date").asText() : (fee.has("date") ? " on " + fee.get("date").asText() : "");
|
||||
String type = fee.has("type") ? fee.get("type").asText() :
|
||||
(fee.has("description") ? fee.get("description").asText() : (fee.has("name") ? fee.get("name").asText() : "Academic Fee"));
|
||||
sb.append("- *").append(type).append("*: ₹").append(amount).append(" (Paid").append(date).append(")\n");
|
||||
foundAcad = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (historyNode.isArray()) {
|
||||
for (JsonNode fee : historyNode) {
|
||||
String amount = fee.has("paid_amt") ? fee.get("paid_amt").asText() : (fee.has("amount") ? fee.get("amount").asText() : "");
|
||||
String date = fee.has("paid_date") ? " on " + fee.get("paid_date").asText() : (fee.has("date") ? " on " + fee.get("date").asText() : "");
|
||||
String type = fee.has("type") ? fee.get("type").asText() :
|
||||
(fee.has("description") ? fee.get("description").asText() : "Academic Fee");
|
||||
sb.append("- *").append(type).append("*: ₹").append(amount).append(" (Paid").append(date).append(")\n");
|
||||
foundAcad = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
if (!foundAcad) {
|
||||
sb.append("No academic fee payment history found.\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String getNumberEmoji(int num) {
|
||||
switch (num) {
|
||||
case 1: return "1️⃣";
|
||||
|
||||
@@ -19,4 +19,4 @@ spring.h2.console.enabled=true
|
||||
|
||||
# Encryption Secret for Passcodes (Must be 16, 24, or 32 bytes)
|
||||
bot.encryption.secret=12345678901234567890123456789012
|
||||
whatsapp.api-token=EAAc3Wjql00oBSUjYhQ1TZBiNJNmwNlpktCtJpqSLXycDVjWjPiP5dBWHvvFX2Deq1tGXQO2jgVOGChnJk9jsMxLVdxvUs4z7g7xlfcQtAPYiHwvQbPVSbGTTCBvjv2IvHf7Ql2iNBKL72GZCrxAJrQpmjHUG5sbQkVWmMpNfwgI52iDBw0BifU1QhQHFEOnQfiPu19KUp3hJOg6XkUbbXyfBKvbSkZB57xj0XHMv6wgpqDrQwAACOjSlSPrNEC3eZB9DbZAqWYnPkJCsX99Pz3GhA
|
||||
whatsapp.api-token=EAAc3Wjql00oBSfSg58drijkqefLn47aI2vYcl8r6GxuwC5vJRt2G8Std3ynf3KnKp9SyuwKXAQfr2Q7hYrNOE66t3dRcS3GAWFbDbikrmvXkbjpIh5V9QNVGI2T4V1VeJAj9d6HMsiHZCAJujaIVLdLleIPti4ok9brnNf1BPwBZA8SZAQ06OUcbSba3SZAQhcoFsh1Utz1g9PLCqsl8Fh9H8ynVJagAxMYqF86sV7fEHKpG6eCp2EkRtfOoE7XbL5AurGkiZA5ZBHiSZCOOujplm3cmgZDZD
|
||||
|
||||
Reference in New Issue
Block a user