littleshop/LittleShop/Controllers/ShippingController.cs

284 lines
9.9 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using LittleShop.Services;
using LittleShop.DTOs;
using LittleShop.Enums;
namespace LittleShop.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = "Bearer")]
public class ShippingController : ControllerBase
{
private readonly IRoyalMailService _royalMailService;
private readonly IShippingRateService _shippingRateService;
private readonly IOrderService _orderService;
private readonly ILogger<ShippingController> _logger;
public ShippingController(
IRoyalMailService royalMailService,
IShippingRateService shippingRateService,
IOrderService orderService,
ILogger<ShippingController> logger)
{
_royalMailService = royalMailService;
_shippingRateService = shippingRateService;
_orderService = orderService;
_logger = logger;
}
[HttpPost("calculate")]
[AllowAnonymous]
public async Task<ActionResult<ShippingCalculationResult>> CalculateShipping([FromBody] CalculateShippingRequest request)
{
try
{
// First try Royal Mail services
var services = await _royalMailService.GetAvailableServicesAsync(request.WeightInGrams, request.Country);
if (services.Any())
{
var result = new ShippingCalculationResult
{
Success = true,
Services = services.Select(s => new ShippingOption
{
ServiceCode = s.Code,
ServiceName = s.Name,
Description = s.Description,
Cost = s.Price,
EstimatedDeliveryDays = s.EstimatedDeliveryDays,
SupportsTracking = s.SupportsTracking
}).ToList()
};
return Ok(result);
}
// Fallback to local shipping rates
var fallbackRate = await _shippingRateService.CalculateShippingAsync(request.WeightInGrams / 1000, request.Country);
if (fallbackRate != null)
{
var fallbackResult = new ShippingCalculationResult
{
Success = true,
Services = new List<ShippingOption>
{
new ShippingOption
{
ServiceCode = "Standard",
ServiceName = fallbackRate.Name,
Description = fallbackRate.Description ?? "Standard shipping",
Cost = fallbackRate.Price,
EstimatedDeliveryDays = (fallbackRate.MinDeliveryDays + fallbackRate.MaxDeliveryDays) / 2,
SupportsTracking = false
}
}
};
return Ok(fallbackResult);
}
return BadRequest(new { Error = "No shipping options available for this destination" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error calculating shipping for weight {Weight}g to {Country}",
request.WeightInGrams, request.Country);
return StatusCode(500, new { Error = "Error calculating shipping costs" });
}
}
[HttpPost("create")]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<CreateShipmentResult>> CreateShipment([FromBody] CreateShipmentRequest request)
{
try
{
var result = await _royalMailService.CreateShipmentAsync(request);
if (result.Success)
{
_logger.LogInformation("Created shipment {ShipmentId} with tracking {TrackingNumber}",
result.ShipmentId, result.TrackingNumber);
return Ok(result);
}
else
{
return BadRequest(new { Error = result.ErrorMessage });
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating shipment");
return StatusCode(500, new { Error = "Error creating shipment" });
}
}
[HttpPost("orders/{orderId}/ship")]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<CreateShipmentResult>> ShipOrder(Guid orderId, [FromBody] ShipOrderRequest request)
{
try
{
// Get the order
var order = await _orderService.GetOrderByIdAsync(orderId);
if (order == null)
{
return NotFound(new { Error = "Order not found" });
}
// Calculate total weight from order items
var totalWeight = order.Items.Sum(item =>
{
// Estimate weight if not available (you might want to add weight to ProductDto)
return item.Quantity * 200; // Default 200g per item
});
// Create shipment request
var shipmentRequest = new CreateShipmentRequest
{
RecipientName = order.ShippingName,
AddressLine1 = order.ShippingAddress,
City = order.ShippingCity,
PostCode = order.ShippingPostCode,
Country = order.ShippingCountry,
WeightInGrams = totalWeight,
Value = order.TotalAmount,
ServiceCode = request.ServiceCode ?? "1st Class",
Reference = $"Order-{order.Id}"
};
var result = await _royalMailService.CreateShipmentAsync(shipmentRequest);
if (result.Success && !string.IsNullOrEmpty(result.TrackingNumber))
{
// Update order with tracking information
var updateRequest = new UpdateOrderStatusDto
{
Status = Enums.OrderStatus.Shipped,
TrackingNumber = result.TrackingNumber
};
await _orderService.UpdateOrderStatusAsync(orderId, updateRequest);
_logger.LogInformation("Order {OrderId} shipped with tracking {TrackingNumber}",
orderId, result.TrackingNumber);
}
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error shipping order {OrderId}", orderId);
return StatusCode(500, new { Error = "Error creating shipment for order" });
}
}
[HttpGet("track/{trackingNumber}")]
[AllowAnonymous]
public async Task<ActionResult<TrackingResult>> GetTrackingInfo(string trackingNumber)
{
try
{
var result = await _royalMailService.GetTrackingInfoAsync(trackingNumber);
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting tracking info for {TrackingNumber}", trackingNumber);
return StatusCode(500, new { Error = "Error retrieving tracking information" });
}
}
[HttpGet("services")]
[AllowAnonymous]
public async Task<ActionResult<List<RoyalMailServiceOption>>> GetAvailableServices(
[FromQuery] decimal weight = 500,
[FromQuery] string country = "United Kingdom")
{
try
{
var services = await _royalMailService.GetAvailableServicesAsync(weight, country);
return Ok(services);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting Royal Mail services");
return StatusCode(500, new { Error = "Error retrieving shipping services" });
}
}
[HttpGet("labels/{shipmentId}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetShippingLabel(string shipmentId)
{
try
{
var labelData = await _royalMailService.GenerateShippingLabelAsync(shipmentId);
if (labelData != null)
{
return File(labelData, "application/pdf", $"shipping-label-{shipmentId}.pdf");
}
return NotFound(new { Error = "Shipping label not found" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating shipping label for {ShipmentId}", shipmentId);
return StatusCode(500, new { Error = "Error generating shipping label" });
}
}
[HttpDelete("{shipmentId}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> CancelShipment(string shipmentId)
{
try
{
var result = await _royalMailService.CancelShipmentAsync(shipmentId);
if (result)
{
return Ok(new { Message = "Shipment cancelled successfully" });
}
return BadRequest(new { Error = "Failed to cancel shipment" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error cancelling shipment {ShipmentId}", shipmentId);
return StatusCode(500, new { Error = "Error cancelling shipment" });
}
}
}
public class CalculateShippingRequest
{
public decimal WeightInGrams { get; set; }
public string Country { get; set; } = "United Kingdom";
}
public class ShipOrderRequest
{
public string? ServiceCode { get; set; }
}
public class ShippingCalculationResult
{
public bool Success { get; set; }
public List<ShippingOption> Services { get; set; } = new();
public string? ErrorMessage { get; set; }
}
public class ShippingOption
{
public string ServiceCode { get; set; } = string.Empty;
public string ServiceName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public decimal Cost { get; set; }
public int EstimatedDeliveryDays { get; set; }
public bool SupportsTracking { get; set; }
}