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.