Search bugs are fixed. Added Global searching parameter

This commit is contained in:
Sidharth Prabhu
2026-06-23 10:47:15 +05:30
parent 217f523059
commit a914fd44cc
16 changed files with 1520 additions and 507 deletions

View File

@@ -73,7 +73,7 @@ public class SecurityConfig {
.requestMatchers(HttpMethod.GET, "/api/orders/user/**").authenticated()
.requestMatchers(HttpMethod.GET, "/api/wallet/balance/**").authenticated()
.requestMatchers(HttpMethod.GET, "/api/wallet/transactions/**").authenticated()
.requestMatchers(HttpMethod.POST, "/api/wallet/topup").hasAnyRole("MASTER", "MANAGER", "STAFF")
.requestMatchers(HttpMethod.POST, "/api/wallet/topup").authenticated()
.requestMatchers(HttpMethod.POST, "/api/coupons/redeem").authenticated()
.requestMatchers(HttpMethod.POST, "/api/feedback/**").authenticated()
.requestMatchers(HttpMethod.GET, "/api/feedback/**").authenticated()
@@ -107,7 +107,18 @@ public class SecurityConfig {
List<String> origins = Arrays.asList(allowedOriginsStr.split(","));
configuration.setAllowedOrigins(origins);
configuration.setAllowedOriginPatterns(List.of(
"http://localhost:*"
"http://localhost:*",
"http://127.0.0.1:*",
"http://192.168.*:*",
"http://10.*:*",
"http://172.*:*",
"http://*.local:*",
"https://localhost:*",
"https://127.0.0.1:*",
"https://192.168.*:*",
"https://10.*:*",
"https://172.*:*",
"https://*.local:*"
));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));

View File

@@ -261,6 +261,12 @@ public class OrderController {
if (tokenUserId != null && !tokenUserId.equals(existingOrder.getUserId()) && !isStaff()) {
return ResponseEntity.status(403).body(Map.of("error", "Access denied"));
}
// Cannot modify completed/delivered orders
if ("COMPLETED".equalsIgnoreCase(existingOrder.getStatus()) || "DELIVERED".equalsIgnoreCase(existingOrder.getStatus())) {
return ResponseEntity.status(400).body(Map.of("error", "Cannot modify a completed or delivered order"));
}
BigDecimal oldAmount = existingOrder.getTotalAmount();
BigDecimal newAmount = updatedOrder.getTotalAmount();

View File

@@ -43,12 +43,13 @@ public class TerminalController {
public List<TerminalDTO> getAllTerminals() {
return terminalService.getAllTerminals().stream()
.map(t -> new TerminalDTO(
t.getId(),
t.getName(),
t.getLocation(),
"********",
t.getId(),
t.getName(),
t.getLocation(),
"********",
"****",
t.isPaired(),
t.isBlocked(),
t.getDeviceId() != null ? maskDeviceId(t.getDeviceId()) : null,
t.getPairedAt()
))
@@ -149,30 +150,39 @@ public class TerminalController {
@RequestParam("paymentId") String paymentId,
@RequestHeader(value = "X-API-KEY", required = false) String apiKeyHeader,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
String apiKey = extractApiKey(apiKeyHeader, authHeader);
if (apiKey == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid or missing token"));
}
// 1. Verify API Key
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
if (terminal.isEmpty()) {
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
if (terminalOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
}
Terminal terminal = terminalOpt.get();
// 2. Parse and Clean payment IDs
// 2. Check if terminal is blocked
if (terminal.isBlocked()) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
"status", "BLOCKED",
"message", "This terminal is out of order"
));
}
// 3. Parse and Clean payment IDs
List<String> orderNumbers = parseAndCleanOrderNumbers(paymentId);
if (orderNumbers.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "paymentId required"));
}
// 3. Try to fetch the first order that exists
// 4. Try to fetch the first order that exists
for (String orderNum : orderNumbers) {
Optional<Order> orderOpt = orderRepository.findByOrderNumber(orderNum);
if (orderOpt.isPresent()) {
Order order = orderOpt.get();
// Check if order is expired/archived
if (order.isArchived()) {
continue; // Check other IDs if available, or return GONE
@@ -183,7 +193,7 @@ public class TerminalController {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("message", "This order has already been fulfilled and cannot be printed again."));
}
return ResponseEntity.ok(order);
}
}
@@ -196,30 +206,39 @@ public class TerminalController {
@PathVariable("orderNumber") String orderNumber,
@RequestHeader(value = "X-API-KEY", required = false) String apiKeyHeader,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
String apiKey = extractApiKey(apiKeyHeader, authHeader);
if (apiKey == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid or missing token"));
}
// 1. Verify API Key
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
if (terminal.isEmpty()) {
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
if (terminalOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
}
Terminal terminal = terminalOpt.get();
// 2. Parse and Clean order numbers
// 2. Check if terminal is blocked
if (terminal.isBlocked()) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
"status", "BLOCKED",
"message", "This terminal is out of order"
));
}
// 3. Parse and Clean order numbers
List<String> orderNumbers = parseAndCleanOrderNumbers(orderNumber);
if (orderNumbers.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
}
// 3. Try to fetch the first order that exists
// 4. Try to fetch the first order that exists
for (String orderNum : orderNumbers) {
Optional<Order> orderOpt = orderRepository.findByOrderNumber(orderNum);
if (orderOpt.isPresent()) {
Order order = orderOpt.get();
// Check if order is expired/archived
if (order.isArchived()) {
return ResponseEntity.status(HttpStatus.GONE)
@@ -231,7 +250,7 @@ public class TerminalController {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("message", "This order has already been fulfilled and cannot be printed again."));
}
return ResponseEntity.ok(order);
}
}
@@ -245,7 +264,7 @@ public class TerminalController {
@RequestBody Map<String, String> body,
@RequestHeader(value = "X-API-KEY", required = false) String apiKeyHeader,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
String orderNumber = body.get("orderNumber");
if (orderNumber == null || orderNumber.isBlank()) {
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
@@ -255,20 +274,29 @@ public class TerminalController {
if (apiKey == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid or missing token"));
}
// 1. Verify API Key
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
if (terminal.isEmpty()) {
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
if (terminalOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
}
Terminal terminal = terminalOpt.get();
// 2. Parse and Clean order numbers
// 2. Check if terminal is blocked
if (terminal.isBlocked()) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
"status", "BLOCKED",
"message", "This terminal is out of order"
));
}
// 3. Parse and Clean order numbers
List<String> orderNumbers = parseAndCleanOrderNumbers(orderNumber);
if (orderNumbers.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
}
// 3. Process all found orders
// 4. Process all found orders
List<String> successfulOrders = new ArrayList<>();
boolean alreadyCompleted = false;
boolean archivedFound = false;
@@ -277,7 +305,7 @@ public class TerminalController {
Optional<Order> orderOpt = orderRepository.findByOrderNumber(orderNum);
if (orderOpt.isPresent()) {
Order order = orderOpt.get();
if (order.isArchived()) {
archivedFound = true;
continue;
@@ -297,8 +325,8 @@ public class TerminalController {
}
if (!successfulOrders.isEmpty()) {
String msg = alreadyCompleted && successfulOrders.size() == 1
? "Order was already marked as delivered."
String msg = alreadyCompleted && successfulOrders.size() == 1
? "Order was already marked as delivered."
: "Order(s) marked as delivered successfully: " + String.join(", ", successfulOrders);
return ResponseEntity.ok(Map.of("success", true, "message", msg));
}
@@ -317,7 +345,7 @@ public class TerminalController {
@PathVariable("orderNumber") String orderNumber,
@RequestHeader(value = "X-API-KEY", required = false) String apiKeyHeader,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
if (orderNumber == null || orderNumber.isBlank()) {
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
}
@@ -326,20 +354,29 @@ public class TerminalController {
if (apiKey == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid or missing token"));
}
// 1. Verify API Key
Optional<Terminal> terminal = terminalRepository.findByApiKey(apiKey);
if (terminal.isEmpty()) {
Optional<Terminal> terminalOpt = terminalRepository.findByApiKey(apiKey);
if (terminalOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid API Key"));
}
Terminal terminal = terminalOpt.get();
// 2. Parse and Clean order numbers
// 2. Check if terminal is blocked
if (terminal.isBlocked()) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
"status", "BLOCKED",
"message", "This terminal is out of order"
));
}
// 3. Parse and Clean order numbers
List<String> orderNumbers = parseAndCleanOrderNumbers(orderNumber);
if (orderNumbers.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "orderNumber required"));
}
// 3. Process all found orders
// 4. Process all found orders
List<String> successfulOrders = new ArrayList<>();
boolean alreadyCompleted = false;
boolean archivedFound = false;
@@ -348,7 +385,7 @@ public class TerminalController {
Optional<Order> orderOpt = orderRepository.findByOrderNumber(orderNum);
if (orderOpt.isPresent()) {
Order order = orderOpt.get();
if (order.isArchived()) {
archivedFound = true;
continue;
@@ -368,8 +405,8 @@ public class TerminalController {
}
if (!successfulOrders.isEmpty()) {
String msg = alreadyCompleted && successfulOrders.size() == 1
? "Order was already marked as delivered."
String msg = alreadyCompleted && successfulOrders.size() == 1
? "Order was already marked as delivered."
: "Order(s) marked as delivered successfully: " + String.join(", ", successfulOrders);
return ResponseEntity.ok(Map.of("success", true, "message", msg));
}
@@ -394,7 +431,8 @@ public class TerminalController {
"status", "VALID",
"terminalId", t.getId(),
"name", t.getName(),
"location", t.getLocation()
"location", t.getLocation(),
"blocked", t.isBlocked()
));
}
@@ -493,6 +531,20 @@ public class TerminalController {
}
}
@PutMapping("/{id}/block")
public ResponseEntity<?> toggleBlockStatus(@PathVariable Long id) {
boolean newStatus = terminalService.toggleBlockStatus(id);
if (terminalService.getTerminalById(id).isPresent()) {
return ResponseEntity.ok(Map.of(
"success", true,
"blocked", newStatus,
"message", newStatus ? "Terminal blocked" : "Terminal unblocked"
));
} else {
return ResponseEntity.notFound().build();
}
}
// ──────────────────────────────────────────────────────────────
// Helpers
// ──────────────────────────────────────────────────────────────

View File

@@ -30,6 +30,9 @@ public class Terminal {
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean paired = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean blocked = false;
@Column(name = "paired_at")
private LocalDateTime pairedAt;
@@ -64,6 +67,9 @@ public class Terminal {
public boolean isPaired() { return paired; }
public void setPaired(boolean paired) { this.paired = paired; }
public boolean isBlocked() { return blocked; }
public void setBlocked(boolean blocked) { this.blocked = blocked; }
public LocalDateTime getPairedAt() { return pairedAt; }
public void setPairedAt(LocalDateTime pairedAt) { this.pairedAt = pairedAt; }
}

View File

@@ -9,19 +9,21 @@ public class TerminalDTO {
private String apiKey;
private String pin;
private boolean paired;
private boolean blocked;
private String deviceId;
private LocalDateTime pairedAt;
public TerminalDTO() {}
public TerminalDTO(Long id, String name, String location, String apiKey, String pin,
boolean paired, String deviceId, LocalDateTime pairedAt) {
boolean paired, boolean blocked, String deviceId, LocalDateTime pairedAt) {
this.id = id;
this.name = name;
this.location = location;
this.apiKey = apiKey;
this.pin = pin;
this.paired = paired;
this.blocked = blocked;
this.deviceId = deviceId;
this.pairedAt = pairedAt;
}
@@ -45,6 +47,9 @@ public class TerminalDTO {
public boolean isPaired() { return paired; }
public void setPaired(boolean paired) { this.paired = paired; }
public boolean isBlocked() { return blocked; }
public void setBlocked(boolean blocked) { this.blocked = blocked; }
public String getDeviceId() { return deviceId; }
public void setDeviceId(String deviceId) { this.deviceId = deviceId; }

View File

@@ -44,6 +44,15 @@ public class TerminalService {
}
}
public boolean toggleBlockStatus(Long id) {
if (id == null) return false;
return terminalRepository.findById(id).map(terminal -> {
terminal.setBlocked(!terminal.isBlocked());
terminalRepository.save(terminal);
return terminal.isBlocked();
}).orElse(false);
}
public Terminal updateTerminal(Long id, Terminal details) {
if (id == null) return null;
return terminalRepository.findById(id).map(existing -> {