feat: Add REST API controllers for materials
Adds MaterialsController with bulk import support and SeedController for development data seeding. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
170
CutList.Web/Controllers/MaterialsController.cs
Normal file
170
CutList.Web/Controllers/MaterialsController.cs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
using CutList.Web.Data;
|
||||||
|
using CutList.Web.Data.Entities;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CutList.Web.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class MaterialsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
public MaterialsController(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<IEnumerable<MaterialDto>>> GetMaterials()
|
||||||
|
{
|
||||||
|
var materials = await _context.Materials
|
||||||
|
.Where(m => m.IsActive)
|
||||||
|
.OrderBy(m => m.Shape)
|
||||||
|
.ThenBy(m => m.Size)
|
||||||
|
.Select(m => new MaterialDto
|
||||||
|
{
|
||||||
|
Id = m.Id,
|
||||||
|
Shape = m.Shape,
|
||||||
|
Size = m.Size,
|
||||||
|
Description = m.Description
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(materials);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<MaterialDto>> GetMaterial(int id)
|
||||||
|
{
|
||||||
|
var material = await _context.Materials.FindAsync(id);
|
||||||
|
|
||||||
|
if (material == null || !material.IsActive)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(new MaterialDto
|
||||||
|
{
|
||||||
|
Id = material.Id,
|
||||||
|
Shape = material.Shape,
|
||||||
|
Size = material.Size,
|
||||||
|
Description = material.Description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<MaterialDto>> CreateMaterial(CreateMaterialDto dto)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(dto.Shape))
|
||||||
|
return BadRequest("Shape is required");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(dto.Size))
|
||||||
|
return BadRequest("Size is required");
|
||||||
|
|
||||||
|
// Check for duplicates
|
||||||
|
var exists = await _context.Materials
|
||||||
|
.AnyAsync(m => m.Shape == dto.Shape && m.Size == dto.Size && m.IsActive);
|
||||||
|
|
||||||
|
if (exists)
|
||||||
|
return Conflict($"Material '{dto.Shape} - {dto.Size}' already exists");
|
||||||
|
|
||||||
|
var material = new Material
|
||||||
|
{
|
||||||
|
Shape = dto.Shape,
|
||||||
|
Size = dto.Size,
|
||||||
|
Description = dto.Description,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Materials.Add(material);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(GetMaterial), new { id = material.Id }, new MaterialDto
|
||||||
|
{
|
||||||
|
Id = material.Id,
|
||||||
|
Shape = material.Shape,
|
||||||
|
Size = material.Size,
|
||||||
|
Description = material.Description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("bulk")]
|
||||||
|
public async Task<ActionResult<BulkCreateResult>> CreateMaterialsBulk(List<CreateMaterialDto> materials)
|
||||||
|
{
|
||||||
|
var created = 0;
|
||||||
|
var skipped = 0;
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
foreach (var dto in materials)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(dto.Shape) || string.IsNullOrWhiteSpace(dto.Size))
|
||||||
|
{
|
||||||
|
errors.Add($"Invalid material: Shape and Size are required");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var exists = await _context.Materials
|
||||||
|
.AnyAsync(m => m.Shape == dto.Shape && m.Size == dto.Size && m.IsActive);
|
||||||
|
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Materials.Add(new Material
|
||||||
|
{
|
||||||
|
Shape = dto.Shape,
|
||||||
|
Size = dto.Size,
|
||||||
|
Description = dto.Description,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
created++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
return Ok(new BulkCreateResult
|
||||||
|
{
|
||||||
|
Created = created,
|
||||||
|
Skipped = skipped,
|
||||||
|
Errors = errors
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> DeleteMaterial(int id)
|
||||||
|
{
|
||||||
|
var material = await _context.Materials.FindAsync(id);
|
||||||
|
|
||||||
|
if (material == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
material.IsActive = false;
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MaterialDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Shape { get; set; } = string.Empty;
|
||||||
|
public string Size { get; set; } = string.Empty;
|
||||||
|
public string? Description { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateMaterialDto
|
||||||
|
{
|
||||||
|
public string Shape { get; set; } = string.Empty;
|
||||||
|
public string Size { get; set; } = string.Empty;
|
||||||
|
public string? Description { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BulkCreateResult
|
||||||
|
{
|
||||||
|
public int Created { get; set; }
|
||||||
|
public int Skipped { get; set; }
|
||||||
|
public List<string> Errors { get; set; } = new();
|
||||||
|
}
|
||||||
94
CutList.Web/Controllers/SeedController.cs
Normal file
94
CutList.Web/Controllers/SeedController.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using CutList.Web.Data;
|
||||||
|
using CutList.Web.Data.Entities;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CutList.Web.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class SeedController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
public SeedController(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("alro-1018-round")]
|
||||||
|
public async Task<ActionResult> SeedAlro1018Round()
|
||||||
|
{
|
||||||
|
// Add Alro supplier if not exists
|
||||||
|
var alro = await _context.Suppliers.FirstOrDefaultAsync(s => s.Name == "Alro");
|
||||||
|
if (alro == null)
|
||||||
|
{
|
||||||
|
alro = new Supplier
|
||||||
|
{
|
||||||
|
Name = "Alro",
|
||||||
|
ContactInfo = "https://www.alro.com",
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
_context.Suppliers.Add(alro);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1018 CF Round bar sizes from the screenshot
|
||||||
|
var sizes = new[]
|
||||||
|
{
|
||||||
|
"1/8\"",
|
||||||
|
"5/32\"",
|
||||||
|
"3/16\"",
|
||||||
|
"7/32\"",
|
||||||
|
".236\"",
|
||||||
|
"1/4\"",
|
||||||
|
"9/32\"",
|
||||||
|
"5/16\"",
|
||||||
|
"11/32\"",
|
||||||
|
"3/8\"",
|
||||||
|
".394\"",
|
||||||
|
"13/32\"",
|
||||||
|
"7/16\"",
|
||||||
|
"15/32\"",
|
||||||
|
".472\"",
|
||||||
|
"1/2\"",
|
||||||
|
"17/32\"",
|
||||||
|
"9/16\"",
|
||||||
|
".593\""
|
||||||
|
};
|
||||||
|
|
||||||
|
var created = 0;
|
||||||
|
var skipped = 0;
|
||||||
|
|
||||||
|
foreach (var size in sizes)
|
||||||
|
{
|
||||||
|
var exists = await _context.Materials
|
||||||
|
.AnyAsync(m => m.Shape == "Round Bar" && m.Size == size && m.IsActive);
|
||||||
|
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Materials.Add(new Material
|
||||||
|
{
|
||||||
|
Shape = "Round Bar",
|
||||||
|
Size = size,
|
||||||
|
Description = "1018 Cold Finished",
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
created++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
Message = "Alro 1018 CF Round materials seeded",
|
||||||
|
SupplierId = alro.Id,
|
||||||
|
MaterialsCreated = created,
|
||||||
|
MaterialsSkipped = skipped
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user