feat(nests): add part search endpoint
Add GET /api/nests/parts/search endpoint to search for parts across nests by part name. Supports filtering by year and customer, with configurable result limit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -180,6 +180,123 @@ public class NestsController : ControllerBase
|
|||||||
return GetNestPath(nestName, year);
|
return GetNestPath(nestName, year);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search for parts across nests by part name.
|
||||||
|
/// If year is not specified, searches across all years.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("parts/search")]
|
||||||
|
public async Task<ActionResult<PartSearchResponse>> SearchParts(
|
||||||
|
[FromQuery] string search,
|
||||||
|
[FromQuery] int? year = null,
|
||||||
|
[FromQuery] string? customer = null,
|
||||||
|
[FromQuery] int limit = 100)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(search))
|
||||||
|
return BadRequest(new { message = "Search term is required" });
|
||||||
|
|
||||||
|
var searchUpper = search.Trim().ToUpper();
|
||||||
|
|
||||||
|
// Get nest headers - filter by year only if specified
|
||||||
|
var nestsQuery = _db.NestHeaders
|
||||||
|
.Where(n => n.DateProgrammed != null);
|
||||||
|
|
||||||
|
if (year.HasValue)
|
||||||
|
nestsQuery = nestsQuery.Where(n => n.DateProgrammed!.Value.Year == year.Value);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(customer))
|
||||||
|
nestsQuery = nestsQuery.Where(n => n.CustomerName == customer || n.CustID == customer);
|
||||||
|
|
||||||
|
var nests = await nestsQuery
|
||||||
|
.Select(n => new
|
||||||
|
{
|
||||||
|
n.NestName,
|
||||||
|
n.CopyID,
|
||||||
|
n.CustomerName,
|
||||||
|
n.Comments,
|
||||||
|
n.Material,
|
||||||
|
n.MatGrade,
|
||||||
|
n.MatThick,
|
||||||
|
n.Status,
|
||||||
|
n.DateProgrammed,
|
||||||
|
n.ModifiedDate,
|
||||||
|
n.Application
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!nests.Any())
|
||||||
|
{
|
||||||
|
return Ok(new PartSearchResponse
|
||||||
|
{
|
||||||
|
SearchTerm = search,
|
||||||
|
Year = year,
|
||||||
|
TotalMatches = 0,
|
||||||
|
TotalNests = 0,
|
||||||
|
Results = []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var nestKeys = nests.Select(n => (n.NestName, n.CopyID)).ToHashSet();
|
||||||
|
|
||||||
|
// Search for parts matching the search term
|
||||||
|
var plateDetails = await _db.PlateDetails
|
||||||
|
.Where(p => p.Drawing != null && p.Drawing.ToUpper().Contains(searchUpper))
|
||||||
|
.Select(p => new
|
||||||
|
{
|
||||||
|
p.NestName,
|
||||||
|
p.CopyID,
|
||||||
|
p.Drawing,
|
||||||
|
p.QtyNstd,
|
||||||
|
p.QtyReq
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Filter to only parts in our target nests
|
||||||
|
var matchingParts = plateDetails
|
||||||
|
.Where(p => nestKeys.Contains((p.NestName, p.CopyID)))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Group by nest and part name
|
||||||
|
var nestLookup = nests.ToDictionary(n => (n.NestName, n.CopyID));
|
||||||
|
|
||||||
|
var allResults = matchingParts
|
||||||
|
.GroupBy(p => (p.NestName, p.CopyID, p.Drawing))
|
||||||
|
.Select(g =>
|
||||||
|
{
|
||||||
|
var nest = nestLookup[(g.Key.NestName, g.Key.CopyID)];
|
||||||
|
return new PartSearchResult
|
||||||
|
{
|
||||||
|
PartName = g.Key.Drawing ?? "",
|
||||||
|
NestName = g.Key.NestName,
|
||||||
|
Status = PepHelper.GetStatus(nest.Status),
|
||||||
|
Customer = nest.CustomerName ?? "",
|
||||||
|
Comments = nest.Comments ?? "",
|
||||||
|
MaterialNumber = int.TryParse(nest.Material, out var num) ? num : 0,
|
||||||
|
MaterialGrade = nest.MatGrade ?? "",
|
||||||
|
Thickness = nest.MatThick,
|
||||||
|
DateProgrammed = nest.DateProgrammed!.Value,
|
||||||
|
DateLastModified = nest.ModifiedDate ?? nest.DateProgrammed!.Value,
|
||||||
|
QtyNested = g.Sum(x => x.QtyNstd ?? 0),
|
||||||
|
QtyRequired = g.Max(x => x.QtyReq ?? 0),
|
||||||
|
Application = PepHelper.GetApplication(nest.Application)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.OrderByDescending(r => r.DateProgrammed)
|
||||||
|
.ThenBy(r => r.PartName)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var limitedResults = limit > 0 ? allResults.Take(limit).ToList() : allResults;
|
||||||
|
|
||||||
|
return Ok(new PartSearchResponse
|
||||||
|
{
|
||||||
|
SearchTerm = search,
|
||||||
|
Year = year,
|
||||||
|
TotalMatches = allResults.Count,
|
||||||
|
TotalNests = allResults.Select(r => r.NestName).Distinct().Count(),
|
||||||
|
ResultsReturned = limitedResults.Count,
|
||||||
|
Results = limitedResults
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private string GetNestPath(string nestName, int year)
|
private string GetNestPath(string nestName, int year)
|
||||||
{
|
{
|
||||||
var fileName = nestName + ".zip";
|
var fileName = nestName + ".zip";
|
||||||
|
|||||||
28
PepApi.Core/Models/PartSearchResult.cs
Normal file
28
PepApi.Core/Models/PartSearchResult.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
namespace PepApi.Core.Models;
|
||||||
|
|
||||||
|
public class PartSearchResult
|
||||||
|
{
|
||||||
|
public required string PartName { get; set; }
|
||||||
|
public required string NestName { get; set; }
|
||||||
|
public required string Status { get; set; }
|
||||||
|
public required string Customer { get; set; }
|
||||||
|
public required string Comments { get; set; }
|
||||||
|
public int MaterialNumber { get; set; }
|
||||||
|
public required string MaterialGrade { get; set; }
|
||||||
|
public double Thickness { get; set; }
|
||||||
|
public DateTime DateProgrammed { get; set; }
|
||||||
|
public DateTime DateLastModified { get; set; }
|
||||||
|
public int QtyNested { get; set; }
|
||||||
|
public int QtyRequired { get; set; }
|
||||||
|
public required string Application { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PartSearchResponse
|
||||||
|
{
|
||||||
|
public required string SearchTerm { get; set; }
|
||||||
|
public int? Year { get; set; }
|
||||||
|
public int TotalMatches { get; set; }
|
||||||
|
public int TotalNests { get; set; }
|
||||||
|
public int ResultsReturned { get; set; }
|
||||||
|
public List<PartSearchResult> Results { get; set; } = [];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user