284 lines
9.9 KiB
C#
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; }
|
|
} |