diff --git a/src/PowderCoating.Web/Controllers/JobsController.cs b/src/PowderCoating.Web/Controllers/JobsController.cs index 3f20fc3..d1bcaf3 100644 --- a/src/PowderCoating.Web/Controllers/JobsController.cs +++ b/src/PowderCoating.Web/Controllers/JobsController.cs @@ -498,6 +498,13 @@ public class JobsController : Controller .OrderByDescending(t => t.TransactionDate).ToList(); ViewBag.MaterialsUsed = allJobTransactions; + // Inventory items for the manual log-material modal + var inventoryItemsForModal = (await _unitOfWork.InventoryItems.GetAllAsync()) + .OrderBy(i => i.Name) + .Select(i => new { i.Id, i.Name, i.UnitOfMeasure, i.QuantityOnHand }) + .ToList(); + ViewBag.InventoryItemsForModal = System.Text.Json.JsonSerializer.Serialize(inventoryItemsForModal); + // Pre-logged powder grouped by InventoryItemId (for Complete Job modal pre-fill) ViewBag.PreLoggedPowder = allJobTransactions .GroupBy(t => t.InventoryItemId) @@ -4080,9 +4087,87 @@ public class JobsController : Controller _logger.LogInformation("Recorded first job creation for company {CompanyId}", companyId); } + + /// + /// Logs manual material usage from the job details page. Mirrors the QR scan LogUsage + /// flow in InventoryController but returns JSON so the modal can close and refresh inline. + /// Quantity is always the amount USED (caller converts from remaining if needed). + /// + [HttpPost] + [ValidateAntiForgeryToken] + public async Task LogMaterial([FromBody] LogMaterialRequest req) + { + try + { + if (req.QuantityUsed <= 0) + return Json(new { success = false, message = "Quantity used must be greater than zero." }); + + var item = await _unitOfWork.InventoryItems.GetByIdAsync(req.InventoryItemId); + if (item == null) return Json(new { success = false, message = "Inventory item not found." }); + + var job = await _unitOfWork.Jobs.GetByIdAsync(req.JobId); + if (job == null) return Json(new { success = false, message = "Job not found." }); + + var txnType = req.TransactionType == "Waste" + ? InventoryTransactionType.Waste + : InventoryTransactionType.JobUsage; + + item.QuantityOnHand -= req.QuantityUsed; + item.UpdatedAt = DateTime.UtcNow; + await _unitOfWork.InventoryItems.UpdateAsync(item); + + var txn = new PowderCoating.Core.Entities.InventoryTransaction + { + InventoryItemId = item.Id, + TransactionType = txnType, + Quantity = -req.QuantityUsed, + UnitCost = item.UnitCost, + TotalCost = req.QuantityUsed * item.UnitCost, + TransactionDate = DateTime.UtcNow, + BalanceAfter = item.QuantityOnHand, + JobId = req.JobId, + Reference = $"Job {job.JobNumber}", + Notes = req.Notes?.Trim(), + CompanyId = item.CompanyId, + CreatedAt = DateTime.UtcNow + }; + await _unitOfWork.InventoryTransactions.AddAsync(txn); + await _unitOfWork.CompleteAsync(); + + // GL: DR COGS, CR Inventory Asset + if (item.CogsAccountId.HasValue && item.InventoryAccountId.HasValue) + { + var cost = req.QuantityUsed * (item.AverageCost > 0 ? item.AverageCost : item.UnitCost); + await _accountBalanceService.DebitAsync(item.CogsAccountId, cost); + await _accountBalanceService.CreditAsync(item.InventoryAccountId, cost); + } + + return Json(new + { + success = true, + message = $"Logged {req.QuantityUsed:N2} {item.UnitOfMeasure} of {item.Name}.", + newBalance = item.QuantityOnHand, + unitOfMeasure = item.UnitOfMeasure, + itemName = item.Name + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error logging material for job {JobId}", req.JobId); + return Json(new { success = false, message = "An error occurred. Please try again." }); + } + } } public class DeleteTimeEntryRequest { public int Id { get; set; } } +public class LogMaterialRequest +{ + public int JobId { get; set; } + public int InventoryItemId { get; set; } + public decimal QuantityUsed { get; set; } + public string TransactionType { get; set; } = "JobUsage"; + public string? Notes { get; set; } +} public class CreateReworkJobRequest { public int ReworkRecordId { get; set; } public string? Notes { get; set; } } public class UpdateWorkerAssignmentRequest diff --git a/src/PowderCoating.Web/Views/Jobs/Details.cshtml b/src/PowderCoating.Web/Views/Jobs/Details.cshtml index 9ddbb86..49b0b17 100644 --- a/src/PowderCoating.Web/Views/Jobs/Details.cshtml +++ b/src/PowderCoating.Web/Views/Jobs/Details.cshtml @@ -1016,9 +1016,12 @@ @materialsUsed.Count } - - - Log Material + + + + @@ -1028,7 +1031,7 @@ {
No materials have been logged for this job yet. - Use the QR label on an inventory item to log usage. + Click Log Material above or scan the QR label on an inventory item.
} else @@ -1089,6 +1092,65 @@ + + + @{ var intakeExpectedCount = Model.Items?.Sum(i => (int)i.Quantity) ?? 0; @@ -3082,6 +3144,18 @@ } } + + + +