From bb35ed1eabe9533ce060ec633ee411fdf35bd8fa Mon Sep 17 00:00:00 2001 From: dex-bot Date: Mon, 27 Apr 2026 20:55:52 -0400 Subject: [PATCH] feat(CUB-9): Implement DELETE /filaments/{id} --- .../API/Controllers/FilamentsController.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/backend/API/Controllers/FilamentsController.cs b/backend/API/Controllers/FilamentsController.cs index f39c81d..f5c1c9c 100644 --- a/backend/API/Controllers/FilamentsController.cs +++ b/backend/API/Controllers/FilamentsController.cs @@ -295,6 +295,66 @@ public class FilamentsController : ControllerBase return Ok(MapToFilamentResponse(entity)); } + /// + /// Deletes a filament spool by its unique identifier. + /// If the spool has associated print jobs, the deletion is rejected with a 409 Conflict + /// to preserve COGS and print history — the caller should archive the spool instead. + /// Associated filament usage records are removed before the spool is deleted. + /// AMS slots referencing this spool will have their SpoolId set to null by the database. + /// + /// The unique identifier of the filament spool to delete. + /// No content on successful deletion. + /// The filament spool was successfully deleted. + /// If the filament spool with the given ID is not found. + /// If the spool has associated print jobs and cannot be deleted. + [HttpDelete("{id:guid}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task DeleteFilament(Guid id) + { + _logger.LogInformation("Deleting filament {Id}", id); + + var entity = await _dbContext.Spools.FindAsync(id); + if (entity is null) + { + _logger.LogWarning("Filament {Id} not found for deletion", id); + return NotFound(new { error = $"Filament with ID '{id}' not found." }); + } + + // Check for associated print jobs — these cannot be orphaned + var hasPrintJobs = await _dbContext.PrintJobs.AnyAsync(pj => pj.SpoolId == id); + if (hasPrintJobs) + { + _logger.LogWarning( + "Cannot delete filament {Id}: associated print jobs exist. Suggest archiving instead.", id); + return Conflict(new + { + error = $"Cannot delete filament '{id}' because it has associated print jobs. " + + "Archive the filament instead to preserve print history and COGS data." + }); + } + + // Remove associated filament usage records (usage tracking data for this spool) + var usageRecords = await _dbContext.FilamentUsages + .Where(fu => fu.SpoolId == id) + .ToListAsync(); + + if (usageRecords.Count > 0) + { + _logger.LogInformation( + "Removing {Count} filament usage records for spool {Id}", + usageRecords.Count, id); + _dbContext.FilamentUsages.RemoveRange(usageRecords); + } + + _dbContext.Spools.Remove(entity); + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("Filament {Id} deleted successfully", id); + return NoContent(); + } + // ── Mapping helper ───────────────────────────────────────── ///