289 lines
8.7 KiB
C#
289 lines
8.7 KiB
C#
using Zentral.API.Modules.Warehouse.Services;
|
|
using Zentral.Domain.Entities.Warehouse;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Zentral.API.Modules.Warehouse.Controllers;
|
|
|
|
/// <summary>
|
|
/// Controller per la gestione delle partite/lotti
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/warehouse/batches")]
|
|
public class BatchesController : ControllerBase
|
|
{
|
|
private readonly IWarehouseService _warehouseService;
|
|
private readonly ILogger<BatchesController> _logger;
|
|
|
|
public BatchesController(
|
|
IWarehouseService warehouseService,
|
|
ILogger<BatchesController> logger)
|
|
{
|
|
_warehouseService = warehouseService;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene la lista delle partite con filtri opzionali
|
|
/// </summary>
|
|
[HttpGet]
|
|
public async Task<ActionResult<List<BatchDto>>> GetBatches(
|
|
[FromQuery] int? articleId = null,
|
|
[FromQuery] BatchStatus? status = null)
|
|
{
|
|
var batches = await _warehouseService.GetBatchesAsync(articleId, status);
|
|
return Ok(batches.Select(MapToDto));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene una partita per ID
|
|
/// </summary>
|
|
[HttpGet("{id}")]
|
|
public async Task<ActionResult<BatchDto>> GetBatch(int id)
|
|
{
|
|
var batch = await _warehouseService.GetBatchByIdAsync(id);
|
|
if (batch == null)
|
|
return NotFound();
|
|
|
|
return Ok(MapToDto(batch));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene una partita per articolo e numero lotto
|
|
/// </summary>
|
|
[HttpGet("by-number/{articleId}/{batchNumber}")]
|
|
public async Task<ActionResult<BatchDto>> GetBatchByNumber(int articleId, string batchNumber)
|
|
{
|
|
var batch = await _warehouseService.GetBatchByNumberAsync(articleId, batchNumber);
|
|
if (batch == null)
|
|
return NotFound();
|
|
|
|
return Ok(MapToDto(batch));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Crea una nuova partita
|
|
/// </summary>
|
|
[HttpPost]
|
|
public async Task<ActionResult<BatchDto>> CreateBatch([FromBody] CreateBatchDto dto)
|
|
{
|
|
try
|
|
{
|
|
var batch = new ArticleBatch
|
|
{
|
|
ArticleId = dto.ArticleId,
|
|
BatchNumber = dto.BatchNumber,
|
|
ProductionDate = dto.ProductionDate,
|
|
ExpiryDate = dto.ExpiryDate,
|
|
SupplierBatch = dto.SupplierBatch,
|
|
SupplierId = dto.SupplierId,
|
|
UnitCost = dto.UnitCost,
|
|
InitialQuantity = dto.InitialQuantity,
|
|
CurrentQuantity = dto.InitialQuantity,
|
|
Status = BatchStatus.Available,
|
|
Certifications = dto.Certifications,
|
|
Notes = dto.Notes
|
|
};
|
|
|
|
var created = await _warehouseService.CreateBatchAsync(batch);
|
|
return CreatedAtAction(nameof(GetBatch), new { id = created.Id }, MapToDto(created));
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return NotFound(new { error = ex.Message });
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return BadRequest(new { error = ex.Message });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aggiorna una partita esistente
|
|
/// </summary>
|
|
[HttpPut("{id}")]
|
|
public async Task<ActionResult<BatchDto>> UpdateBatch(int id, [FromBody] UpdateBatchDto dto)
|
|
{
|
|
try
|
|
{
|
|
var existing = await _warehouseService.GetBatchByIdAsync(id);
|
|
if (existing == null)
|
|
return NotFound();
|
|
|
|
if (dto.ProductionDate.HasValue)
|
|
existing.ProductionDate = dto.ProductionDate;
|
|
if (dto.ExpiryDate.HasValue)
|
|
existing.ExpiryDate = dto.ExpiryDate;
|
|
if (dto.SupplierBatch != null)
|
|
existing.SupplierBatch = dto.SupplierBatch;
|
|
if (dto.UnitCost.HasValue)
|
|
existing.UnitCost = dto.UnitCost;
|
|
if (dto.Certifications != null)
|
|
existing.Certifications = dto.Certifications;
|
|
if (dto.Notes != null)
|
|
existing.Notes = dto.Notes;
|
|
|
|
var updated = await _warehouseService.UpdateBatchAsync(existing);
|
|
return Ok(MapToDto(updated));
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return BadRequest(new { error = ex.Message });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aggiorna lo stato di una partita
|
|
/// </summary>
|
|
[HttpPut("{id}/status")]
|
|
public async Task<ActionResult> UpdateBatchStatus(int id, [FromBody] UpdateBatchStatusDto dto)
|
|
{
|
|
try
|
|
{
|
|
await _warehouseService.UpdateBatchStatusAsync(id, dto.Status);
|
|
return Ok();
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return NotFound(new { error = ex.Message });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene le partite in scadenza
|
|
/// </summary>
|
|
[HttpGet("expiring")]
|
|
public async Task<ActionResult<List<BatchDto>>> GetExpiringBatches([FromQuery] int daysThreshold = 30)
|
|
{
|
|
var batches = await _warehouseService.GetExpiringBatchesAsync(daysThreshold);
|
|
return Ok(batches.Select(MapToDto));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registra un controllo qualità sulla partita
|
|
/// </summary>
|
|
[HttpPost("{id}/quality-check")]
|
|
public async Task<ActionResult<BatchDto>> RecordQualityCheck(int id, [FromBody] QualityCheckDto dto)
|
|
{
|
|
try
|
|
{
|
|
var batch = await _warehouseService.GetBatchByIdAsync(id);
|
|
if (batch == null)
|
|
return NotFound();
|
|
|
|
batch.QualityStatus = dto.QualityStatus;
|
|
batch.LastQualityCheckDate = DateTime.UtcNow;
|
|
|
|
// Aggiorna lo stato del lotto in base al risultato
|
|
if (dto.QualityStatus == QualityStatus.Rejected)
|
|
{
|
|
batch.Status = BatchStatus.Blocked;
|
|
}
|
|
else if (dto.QualityStatus == QualityStatus.Approved && batch.Status == BatchStatus.Quarantine)
|
|
{
|
|
batch.Status = BatchStatus.Available;
|
|
}
|
|
|
|
var updated = await _warehouseService.UpdateBatchAsync(batch);
|
|
return Ok(MapToDto(updated));
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return NotFound(new { error = ex.Message });
|
|
}
|
|
}
|
|
|
|
#region DTOs
|
|
|
|
public record BatchDto(
|
|
int Id,
|
|
int ArticleId,
|
|
string? ArticleCode,
|
|
string? ArticleDescription,
|
|
string BatchNumber,
|
|
DateTime? ProductionDate,
|
|
DateTime? ExpiryDate,
|
|
string? SupplierBatch,
|
|
int? SupplierId,
|
|
decimal? UnitCost,
|
|
decimal InitialQuantity,
|
|
decimal CurrentQuantity,
|
|
decimal ReservedQuantity,
|
|
decimal AvailableQuantity,
|
|
BatchStatus Status,
|
|
QualityStatus? QualityStatus,
|
|
DateTime? LastQualityCheckDate,
|
|
string? Certifications,
|
|
string? Notes,
|
|
bool IsExpired,
|
|
int? DaysToExpiry,
|
|
DateTime? CreatedAt,
|
|
DateTime? UpdatedAt
|
|
);
|
|
|
|
public record CreateBatchDto(
|
|
int ArticleId,
|
|
string BatchNumber,
|
|
DateTime? ProductionDate,
|
|
DateTime? ExpiryDate,
|
|
string? SupplierBatch,
|
|
int? SupplierId,
|
|
decimal? UnitCost,
|
|
decimal InitialQuantity,
|
|
string? Certifications,
|
|
string? Notes
|
|
);
|
|
|
|
public record UpdateBatchDto(
|
|
DateTime? ProductionDate,
|
|
DateTime? ExpiryDate,
|
|
string? SupplierBatch,
|
|
decimal? UnitCost,
|
|
string? Certifications,
|
|
string? Notes
|
|
);
|
|
|
|
public record UpdateBatchStatusDto(BatchStatus Status);
|
|
|
|
public record QualityCheckDto(QualityStatus QualityStatus, string? Notes);
|
|
|
|
#endregion
|
|
|
|
#region Mapping
|
|
|
|
private static BatchDto MapToDto(ArticleBatch batch)
|
|
{
|
|
var isExpired = batch.ExpiryDate.HasValue && batch.ExpiryDate.Value < DateTime.UtcNow;
|
|
var daysToExpiry = batch.ExpiryDate.HasValue
|
|
? (int?)Math.Max(0, (batch.ExpiryDate.Value - DateTime.UtcNow).Days)
|
|
: null;
|
|
|
|
return new BatchDto(
|
|
batch.Id,
|
|
batch.ArticleId,
|
|
batch.Article?.Code,
|
|
batch.Article?.Description,
|
|
batch.BatchNumber,
|
|
batch.ProductionDate,
|
|
batch.ExpiryDate,
|
|
batch.SupplierBatch,
|
|
batch.SupplierId,
|
|
batch.UnitCost,
|
|
batch.InitialQuantity,
|
|
batch.CurrentQuantity,
|
|
batch.ReservedQuantity,
|
|
batch.CurrentQuantity - batch.ReservedQuantity,
|
|
batch.Status,
|
|
batch.QualityStatus,
|
|
batch.LastQualityCheckDate,
|
|
batch.Certifications,
|
|
batch.Notes,
|
|
isExpired,
|
|
daysToExpiry,
|
|
batch.CreatedAt,
|
|
batch.UpdatedAt
|
|
);
|
|
}
|
|
|
|
#endregion
|
|
}
|