From 1f3eb67eb7d7c40768e0c7293a3fd06799811889 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 15 Feb 2026 22:20:53 -0500 Subject: [PATCH] feat: Add job and cutting tool API client methods Add HTTP client methods for job CRUD, parts, stock, packing, and cutting tool endpoints. Includes response DTOs for all job-related API responses. Co-Authored-By: Claude Opus 4.6 --- CutList.Mcp/ApiClient.cs | 232 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 231 insertions(+), 1 deletion(-) diff --git a/CutList.Mcp/ApiClient.cs b/CutList.Mcp/ApiClient.cs index 44419d9..17206ad 100644 --- a/CutList.Mcp/ApiClient.cs +++ b/CutList.Mcp/ApiClient.cs @@ -109,6 +109,109 @@ public class ApiClient #endregion + #region Jobs + + public async Task> GetJobsAsync() + { + return await _http.GetFromJsonAsync>("api/jobs") ?? []; + } + + public async Task GetJobAsync(int id) + { + return await _http.GetFromJsonAsync($"api/jobs/{id}"); + } + + public async Task CreateJobAsync(string? name, string? customer, int? cuttingToolId, string? notes) + { + var response = await _http.PostAsJsonAsync("api/jobs", new { Name = name, Customer = customer, CuttingToolId = cuttingToolId, Notes = notes }); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task UpdateJobAsync(int id, string? name, string? customer, int? cuttingToolId, string? notes) + { + var response = await _http.PutAsJsonAsync($"api/jobs/{id}", new { Name = name, Customer = customer, CuttingToolId = cuttingToolId, Notes = notes }); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task DeleteJobAsync(int id) + { + var response = await _http.DeleteAsync($"api/jobs/{id}"); + response.EnsureSuccessStatusCode(); + } + + public async Task AddJobPartAsync(int jobId, int materialId, string name, string length, int quantity) + { + var response = await _http.PostAsJsonAsync($"api/jobs/{jobId}/parts", new { MaterialId = materialId, Name = name, Length = length, Quantity = quantity }); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task UpdateJobPartAsync(int jobId, int partId, int? materialId, string? name, string? length, int? quantity) + { + var response = await _http.PutAsJsonAsync($"api/jobs/{jobId}/parts/{partId}", new { MaterialId = materialId, Name = name, Length = length, Quantity = quantity }); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task DeleteJobPartAsync(int jobId, int partId) + { + var response = await _http.DeleteAsync($"api/jobs/{jobId}/parts/{partId}"); + response.EnsureSuccessStatusCode(); + } + + public async Task AddJobStockAsync(int jobId, int materialId, int? stockItemId, string length, int quantity, bool isCustomLength, int priority) + { + var response = await _http.PostAsJsonAsync($"api/jobs/{jobId}/stock", new + { + MaterialId = materialId, + StockItemId = stockItemId, + Length = length, + Quantity = quantity, + IsCustomLength = isCustomLength, + Priority = priority + }); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task DeleteJobStockAsync(int jobId, int stockId) + { + var response = await _http.DeleteAsync($"api/jobs/{jobId}/stock/{stockId}"); + response.EnsureSuccessStatusCode(); + } + + public async Task PackJobAsync(int jobId, decimal? kerfOverride = null) + { + var response = await _http.PostAsJsonAsync($"api/jobs/{jobId}/pack", new { KerfOverride = kerfOverride }); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + #endregion + + #region Cutting Tools + + public async Task> GetCuttingToolsAsync(bool includeInactive = false) + { + return await _http.GetFromJsonAsync>($"api/cutting-tools?includeInactive={includeInactive}") ?? []; + } + + public async Task GetDefaultCuttingToolAsync() + { + try + { + return await _http.GetFromJsonAsync("api/cutting-tools/default"); + } + catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) + { + return null; + } + } + + #endregion + #region Offerings public async Task> GetOfferingsForSupplierAsync(int supplierId) @@ -153,7 +256,134 @@ public class ApiConflictException : Exception public ApiConflictException(string message) : base(message) { } } -#region API Response DTOs +#region API Response DTOs — Jobs & Cutting Tools + +public class ApiJobDto +{ + public int Id { get; set; } + public string JobNumber { get; set; } = string.Empty; + public string? Name { get; set; } + public string? Customer { get; set; } + public int? CuttingToolId { get; set; } + public string? CuttingToolName { get; set; } + public string? Notes { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public int PartCount { get; set; } + public int StockCount { get; set; } +} + +public class ApiJobDetailDto : ApiJobDto +{ + public List Parts { get; set; } = new(); + public List Stock { get; set; } = new(); +} + +public class ApiJobPartDto +{ + public int Id { get; set; } + public int JobId { get; set; } + public int MaterialId { get; set; } + public string MaterialName { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public decimal LengthInches { get; set; } + public string LengthFormatted { get; set; } = string.Empty; + public int Quantity { get; set; } + public int SortOrder { get; set; } +} + +public class ApiJobStockDto +{ + public int Id { get; set; } + public int JobId { get; set; } + public int MaterialId { get; set; } + public string MaterialName { get; set; } = string.Empty; + public int? StockItemId { get; set; } + public decimal LengthInches { get; set; } + public string LengthFormatted { get; set; } = string.Empty; + public int Quantity { get; set; } + public bool IsCustomLength { get; set; } + public int Priority { get; set; } + public int SortOrder { get; set; } +} + +public class ApiCuttingToolDto +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public decimal KerfInches { get; set; } + public bool IsDefault { get; set; } + public bool IsActive { get; set; } +} + +public class ApiPackResponseDto +{ + public List Materials { get; set; } = new(); + public ApiPackingSummaryDto Summary { get; set; } = new(); +} + +public class ApiMaterialPackResultDto +{ + public int MaterialId { get; set; } + public string MaterialName { get; set; } = string.Empty; + public List InStockBins { get; set; } = new(); + public List ToBePurchasedBins { get; set; } = new(); + public List ItemsNotPlaced { get; set; } = new(); + public ApiMaterialPackingSummaryDto Summary { get; set; } = new(); +} + +public class ApiPackedBinDto +{ + public double LengthInches { get; set; } + public string LengthFormatted { get; set; } = string.Empty; + public double UsedInches { get; set; } + public string UsedFormatted { get; set; } = string.Empty; + public double WasteInches { get; set; } + public string WasteFormatted { get; set; } = string.Empty; + public double Efficiency { get; set; } + public List Items { get; set; } = new(); +} + +public class ApiPackedItemDto +{ + public string Name { get; set; } = string.Empty; + public double LengthInches { get; set; } + public string LengthFormatted { get; set; } = string.Empty; +} + +public class ApiPackingSummaryDto +{ + public int TotalInStockBins { get; set; } + public int TotalToBePurchasedBins { get; set; } + public int TotalPieces { get; set; } + public double TotalMaterialInches { get; set; } + public string TotalMaterialFormatted { get; set; } = string.Empty; + public double TotalUsedInches { get; set; } + public string TotalUsedFormatted { get; set; } = string.Empty; + public double TotalWasteInches { get; set; } + public string TotalWasteFormatted { get; set; } = string.Empty; + public double Efficiency { get; set; } + public int TotalItemsNotPlaced { get; set; } + public List MaterialSummaries { get; set; } = new(); +} + +public class ApiMaterialPackingSummaryDto +{ + public int MaterialId { get; set; } + public string MaterialName { get; set; } = string.Empty; + public int InStockBins { get; set; } + public int ToBePurchasedBins { get; set; } + public int TotalPieces { get; set; } + public double TotalMaterialInches { get; set; } + public double TotalUsedInches { get; set; } + public double TotalWasteInches { get; set; } + public double Efficiency { get; set; } + public int ItemsNotPlaced { get; set; } +} + +#endregion + +#region API Response DTOs — Inventory public class ApiSupplierDto {