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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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