Fix time entry 500 and inventory edit pencil visibility

- Remove parseInt() from time entry worker select — GUIDs were destroyed
  to NaN → sent as null → FindByIdAsync(null) threw 500
- Ledger pencil: also show for Adjustment rows (no PO) so scan-without-job
  entries get an edit button, not just JobUsage rows
- InventoryController: always write JobUsage type for scan-based logs;
  accept Adjustment in edit endpoints; promote Adjustment→JobUsage when
  a job is assigned via edit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 21:46:05 -04:00
parent 010d0437c2
commit c45a6826bd
3 changed files with 13 additions and 7 deletions
@@ -1424,8 +1424,8 @@ public class InventoryController : Controller
} }
var userId = _userManager.GetUserId(User) ?? string.Empty; var userId = _userManager.GetUserId(User) ?? string.Empty;
var txnType = jobId.HasValue ? InventoryTransactionType.JobUsage // Scan-based logging always records as JobUsage; Adjustment is for manual stock corrections only
: (Enum.TryParse<InventoryTransactionType>(transactionType, out var parsed) ? parsed : InventoryTransactionType.Adjustment); var txnType = InventoryTransactionType.JobUsage;
item.QuantityOnHand -= quantity; item.QuantityOnHand -= quantity;
item.UpdatedAt = DateTime.UtcNow; item.UpdatedAt = DateTime.UtcNow;
@@ -1719,8 +1719,9 @@ public class InventoryController : Controller
var txn = await _unitOfWork.InventoryTransactions.GetByIdAsync(id, false, var txn = await _unitOfWork.InventoryTransactions.GetByIdAsync(id, false,
t => t.Job, t => t.InventoryItem); t => t.Job, t => t.InventoryItem);
if (txn == null) return NotFound(); if (txn == null) return NotFound();
if (txn.TransactionType != InventoryTransactionType.JobUsage) if (txn.TransactionType != InventoryTransactionType.JobUsage
return BadRequest("Only JobUsage transactions can be edited here."); && txn.TransactionType != InventoryTransactionType.Adjustment)
return BadRequest("Only usage transactions can be edited here.");
var allJobs = await _unitOfWork.Jobs.FindAsync( var allJobs = await _unitOfWork.Jobs.FindAsync(
j => !j.JobStatus.IsTerminalStatus, j => !j.JobStatus.IsTerminalStatus,
@@ -1762,7 +1763,8 @@ public class InventoryController : Controller
{ {
var txn = await _unitOfWork.InventoryTransactions.GetByIdAsync(id); var txn = await _unitOfWork.InventoryTransactions.GetByIdAsync(id);
if (txn == null) return NotFound(); if (txn == null) return NotFound();
if (txn.TransactionType != InventoryTransactionType.JobUsage) if (txn.TransactionType != InventoryTransactionType.JobUsage
&& txn.TransactionType != InventoryTransactionType.Adjustment)
return BadRequest(); return BadRequest();
if (jobId.HasValue) if (jobId.HasValue)
@@ -1777,6 +1779,10 @@ public class InventoryController : Controller
txn.Reference = null; txn.Reference = null;
} }
// Promote Adjustment→JobUsage when a job is assigned so it shows in Powder Usage By Job tab
if (jobId.HasValue && txn.TransactionType == InventoryTransactionType.Adjustment)
txn.TransactionType = InventoryTransactionType.JobUsage;
txn.Notes = notes?.Trim(); txn.Notes = notes?.Trim();
txn.TransactionDate = transactionDate.Kind == DateTimeKind.Utc txn.TransactionDate = transactionDate.Kind == DateTimeKind.Utc
? transactionDate : DateTime.SpecifyKind(transactionDate, DateTimeKind.Utc); ? transactionDate : DateTime.SpecifyKind(transactionDate, DateTimeKind.Utc);
@@ -205,7 +205,7 @@
</td> </td>
<td><small class="text-muted">@t.Notes</small></td> <td><small class="text-muted">@t.Notes</small></td>
<td> <td>
@if (t.TransactionType == "JobUsage") @if (t.TransactionType == "JobUsage" || (t.TransactionType == "Adjustment" && t.PurchaseOrderId == null))
{ {
<button type="button" class="btn btn-outline-secondary btn-sm py-0 px-1" <button type="button" class="btn btn-outline-secondary btn-sm py-0 px-1"
title="Edit usage record" title="Edit usage record"
@@ -2795,7 +2795,7 @@
async function save() { async function save() {
const id = parseInt(document.getElementById('teEntryId').value); const id = parseInt(document.getElementById('teEntryId').value);
const workerId = parseInt(document.getElementById('teWorkerId').value); const workerId = document.getElementById('teWorkerId').value;
const workDate = document.getElementById('teWorkDate').value; const workDate = document.getElementById('teWorkDate').value;
const hours = parseFloat(document.getElementById('teHoursWorked').value); const hours = parseFloat(document.getElementById('teHoursWorked').value);
const stage = document.getElementById('teStage').value.trim(); const stage = document.getElementById('teStage').value.trim();