153 lines
5.8 KiB
C#
153 lines
5.8 KiB
C#
using AVSCartonShipmentVerifier.DTOs;
|
|
using AVSCartonShipmentVerifier.Repositories;
|
|
using AVSCartonShipmentVerifier.Services;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace AVSCartonShipmentVerifier.Controllers;
|
|
|
|
[Route("api/verification")]
|
|
public sealed class VerificationController(
|
|
IShipmentVerificationService shipmentVerificationService,
|
|
IScanHistoryStore scanHistoryStore,
|
|
ICartonVerificationRepository cartonVerificationRepository,
|
|
IMovementAnalyticsService movementAnalyticsService,
|
|
ILogger<VerificationController> logger) : Controller
|
|
{
|
|
private readonly IShipmentVerificationService _shipmentVerificationService = shipmentVerificationService;
|
|
private readonly IScanHistoryStore _scanHistoryStore = scanHistoryStore;
|
|
private readonly ICartonVerificationRepository _cartonVerificationRepository = cartonVerificationRepository;
|
|
private readonly IMovementAnalyticsService _movementAnalyticsService = movementAnalyticsService;
|
|
private readonly ILogger<VerificationController> _logger = logger;
|
|
|
|
[HttpPost("scan")]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Scan([FromBody] QrScanRequestDto request, CancellationToken cancellationToken)
|
|
{
|
|
if (request is null)
|
|
{
|
|
return BadRequest(new { message = "Request payload is required." });
|
|
}
|
|
|
|
try
|
|
{
|
|
var verificationResult = await _shipmentVerificationService.VerifyQrAsync(
|
|
request.QrValue,
|
|
request.EntryMethod ?? "Scanner",
|
|
cancellationToken);
|
|
|
|
var availableCartons = await TryGetAvailableCartonCountAsync(cancellationToken);
|
|
|
|
return Ok(new
|
|
{
|
|
result = verificationResult,
|
|
recentSuccessfulScans = await _scanHistoryStore.GetLastFiveSuccessfulScansAsync(cancellationToken),
|
|
recentRejectedScans = await _scanHistoryStore.GetLastFiveRejectedScansAsync(cancellationToken),
|
|
availableCartons
|
|
});
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
return StatusCode(StatusCodes.Status499ClientClosedRequest);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Unexpected error while verifying QR scan.");
|
|
|
|
return StatusCode(StatusCodes.Status500InternalServerError, new
|
|
{
|
|
message = "An unexpected error occurred during verification."
|
|
});
|
|
}
|
|
}
|
|
|
|
[HttpGet("dashboard-summary")]
|
|
public async Task<IActionResult> GetDashboardSummary(CancellationToken cancellationToken)
|
|
{
|
|
var count = await TryGetAvailableCartonCountAsync(cancellationToken);
|
|
if (count is null)
|
|
{
|
|
return StatusCode(StatusCodes.Status503ServiceUnavailable, new { message = "Unable to load available cartons count." });
|
|
}
|
|
|
|
return Ok(new { availableCartons = count.Value });
|
|
}
|
|
|
|
[HttpGet("available-cartons")]
|
|
public async Task<IActionResult> GetAvailableCartons(CancellationToken cancellationToken)
|
|
{
|
|
var count = await TryGetAvailableCartonCountAsync(cancellationToken);
|
|
if (count is null)
|
|
{
|
|
return StatusCode(StatusCodes.Status503ServiceUnavailable, new { message = "Unable to load available cartons count." });
|
|
}
|
|
|
|
return Ok(new { availableCartons = count.Value });
|
|
}
|
|
|
|
[HttpGet("movement-analytics")]
|
|
public async Task<IActionResult> GetMovementAnalytics(
|
|
[FromQuery] DateOnly? from,
|
|
[FromQuery] DateOnly? to,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var today = DateOnly.FromDateTime(DateTime.Now);
|
|
var toDate = to ?? today;
|
|
var fromDate = from ?? toDate.AddDays(-6);
|
|
|
|
try
|
|
{
|
|
var analytics = await _movementAnalyticsService.GetAnalyticsAsync(fromDate, toDate, cancellationToken);
|
|
return Ok(analytics);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Unexpected error while loading movement analytics.");
|
|
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Unable to load movement analytics." });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all successful or all rejected scan records from SQLite (not limited to five).
|
|
/// </summary>
|
|
[HttpGet("history")]
|
|
public async Task<IActionResult> GetFullHistory([FromQuery] string category = "successful", CancellationToken cancellationToken = default)
|
|
{
|
|
var normalized = category.Trim().ToLowerInvariant();
|
|
try
|
|
{
|
|
if (normalized == "successful")
|
|
{
|
|
var records = await _scanHistoryStore.GetAllSuccessfulScansAsync(cancellationToken);
|
|
return Ok(new { category = "successful", records });
|
|
}
|
|
|
|
if (normalized == "rejected")
|
|
{
|
|
var records = await _scanHistoryStore.GetAllRejectedScansAsync(cancellationToken);
|
|
return Ok(new { category = "rejected", records });
|
|
}
|
|
|
|
return BadRequest(new { message = "Invalid category. Use 'successful' or 'rejected'." });
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Unexpected error while loading scan history.");
|
|
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Unable to load scan history." });
|
|
}
|
|
}
|
|
|
|
private async Task<int?> TryGetAvailableCartonCountAsync(CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
return await _cartonVerificationRepository.GetAvailableCartonCountAsync(cancellationToken);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogWarning(exception, "Failed to load available cartons count from verification database.");
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|