Fix time entry workers, powder usage logging, inventory edit, and mojibake
- JobTimeEntry: migrate to UserId/UserDisplayName; make ShopWorkerId nullable (migration MigrateTimeEntriesToUserId) - Log Time modal: populate worker dropdown from Identity users instead of ShopWorkers; fix ShopMobile view same issue - Inventory Ledger: scan-based JobUsage transactions now appear in Powder Usage By Job tab (synthesized from InventoryTransaction) - Inventory Ledger: add Edit button for JobUsage transactions; new GetUsageForEdit + EditUsageTransaction endpoints; inventory-ledger.js - InventoryTransactionRepository: include Job.Customer for ledger queries - InventoryAiLookupService: handle JSON-LD @graph wrapper (Columbia Coatings / WooCommerce+Yoast); add HTML price snippet fallback - Fix mojibake in 9 views: â†' → →, âœ" → ✓, âš → ⚠ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,7 @@
|
||||
<th class="text-end">Balance After</th>
|
||||
<th>Reference</th>
|
||||
<th>Notes</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -203,6 +204,16 @@
|
||||
}
|
||||
</td>
|
||||
<td><small class="text-muted">@t.Notes</small></td>
|
||||
<td>
|
||||
@if (t.TransactionType == "JobUsage")
|
||||
{
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm py-0 px-1"
|
||||
title="Edit usage record"
|
||||
onclick="openUsageEdit(@t.Id)">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
@@ -241,6 +252,7 @@
|
||||
<th class="text-end">Actual (lbs)</th>
|
||||
<th class="text-end">Variance</th>
|
||||
<th>Notes</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -250,10 +262,17 @@
|
||||
<tr>
|
||||
<td class="text-nowrap">@u.RecordedAt.Tz(ViewBag.CompanyTimeZone as string).ToString("MM/dd/yyyy")</td>
|
||||
<td class="text-nowrap">
|
||||
<a asp-controller="Jobs" asp-action="Details" asp-route-id="@u.JobId"
|
||||
class="text-decoration-none fw-semibold">
|
||||
@u.JobNumber
|
||||
</a>
|
||||
@if (u.JobId > 0)
|
||||
{
|
||||
<a asp-controller="Jobs" asp-action="Details" asp-route-id="@u.JobId"
|
||||
class="text-decoration-none fw-semibold">
|
||||
@u.JobNumber
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted fst-italic">No job assigned</span>
|
||||
}
|
||||
</td>
|
||||
<td>@u.CustomerName</td>
|
||||
@if (!Model.InventoryItemId.HasValue)
|
||||
@@ -279,6 +298,16 @@
|
||||
@(variance > 0 ? "+" : "")@variance.ToString("N3")
|
||||
</td>
|
||||
<td><small class="text-muted">@u.Notes</small></td>
|
||||
<td>
|
||||
@if (u.SourceTransactionId.HasValue)
|
||||
{
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm py-0 px-1"
|
||||
title="Edit usage record"
|
||||
onclick="openUsageEdit(@u.SourceTransactionId.Value)">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
@@ -291,6 +320,7 @@
|
||||
@(Model.PowderUsageLogs.Sum(u => u.VarianceLbs) > 0 ? "+" : "")@Model.PowderUsageLogs.Sum(u => u.VarianceLbs).ToString("N3")
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -302,14 +332,63 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
@* ── Edit Usage Modal ─────────────────────────────────────────────── *@
|
||||
<div class="modal fade" id="editUsageModal" tabindex="-1" aria-labelledby="editUsageModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editUsageModalLabel">
|
||||
<i class="bi bi-pencil me-2"></i>Edit Usage Record
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="editUsageLoading" class="text-center py-4">
|
||||
<div class="spinner-border spinner-border-sm me-2"></div>Loading…
|
||||
</div>
|
||||
<form id="editUsageForm" class="d-none">
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" id="euTxnId" name="id" />
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Powder Item</label>
|
||||
<p id="euItemName" class="form-control-plaintext text-muted"></p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="euJobId" class="form-label fw-semibold">Job <span class="text-muted fw-normal">(optional)</span></label>
|
||||
<select id="euJobId" name="jobId" class="form-select">
|
||||
<option value="">— No job —</option>
|
||||
</select>
|
||||
<div class="form-text">Select the job this powder was used on.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="euDate" class="form-label fw-semibold">Date / Time</label>
|
||||
<input type="datetime-local" id="euDate" name="transactionDate" class="form-control" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="euNotes" class="form-label fw-semibold">Notes</label>
|
||||
<textarea id="euNotes" name="notes" class="form-control" rows="2" maxlength="500"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="euSaveBtn" disabled>
|
||||
<span id="euSaveBtnText">Save Changes</span>
|
||||
<span id="euSaveBtnSpinner" class="spinner-border spinner-border-sm ms-1 d-none"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script src="~/js/inventory-ledger.js" asp-append-version="true"></script>
|
||||
<script>
|
||||
function switchTab(tab) {
|
||||
document.getElementById('tab-transactions').classList.toggle('d-none', tab !== 'transactions');
|
||||
document.getElementById('tab-usage').classList.toggle('d-none', tab !== 'usage');
|
||||
document.querySelectorAll('#ledgerTabs .nav-link').forEach(el => el.classList.remove('active'));
|
||||
event.currentTarget.classList.add('active');
|
||||
// Update hidden tab field in filter form
|
||||
document.querySelector('input[name="tab"]').value = tab;
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user