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:
@@ -1,4 +1,4 @@
|
||||
@model PowderCoating.Application.DTOs.Job.JobDto
|
||||
@model PowderCoating.Application.DTOs.Job.JobDto
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Job {Model.JobNumber}";
|
||||
@@ -730,7 +730,6 @@
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Worker</th>
|
||||
<th>Role</th>
|
||||
<th>Date</th>
|
||||
<th class="text-end">Hours</th>
|
||||
<th>Stage</th>
|
||||
@@ -1311,7 +1310,7 @@
|
||||
<a asp-action="Intake" asp-route-id="@Model.Id"
|
||||
class="btn @(Model.IntakeDate.HasValue ? "btn-outline-secondary" : "btn-outline-info")"
|
||||
title="@(Model.IntakeDate.HasValue ? "Update part intake record" : "Check in parts for this job")">
|
||||
<i class="bi bi-box-seam me-2"></i>@(Model.IntakeDate.HasValue ? "Intake ✓" : "Intake")
|
||||
<i class=”bi bi-box-seam me-2”></i>@(Model.IntakeDate.HasValue ? "Intake ✓" : "Intake")
|
||||
</a>
|
||||
}
|
||||
@{
|
||||
@@ -2198,7 +2197,7 @@
|
||||
<option value="">— Select worker —</option>
|
||||
@foreach (var w in (ViewBag.ShopWorkers as IEnumerable<dynamic> ?? []))
|
||||
{
|
||||
<option value="@w.Id">@w.Name (@w.Role)</option>
|
||||
<option value="@w.Id">@w.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@@ -2682,8 +2681,8 @@
|
||||
|
||||
// Notes
|
||||
const notes = [];
|
||||
if (!d.hasPowderData) notes.push('âš Add powder cost per lb on coat records to include material cost.');
|
||||
if (!d.hasLaborData) notes.push('âš Log time entries to include labor cost.');
|
||||
if (!d.hasPowderData) notes.push('⚠ Add powder cost per lb on coat records to include material cost.');
|
||||
if (!d.hasLaborData) notes.push('⚠ Log time entries to include labor cost.');
|
||||
if (d.laborLines?.some(l => l.usingFallback)) notes.push('* One or more workers using standard labor rate fallback.');
|
||||
document.getElementById('costingNotes').innerHTML = notes.map(n => `<div class="text-muted">${n}</div>`).join('');
|
||||
|
||||
@@ -2748,7 +2747,6 @@
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td class="fw-semibold">${esc(e.workerName)}</td>
|
||||
<td class="text-muted small">${esc(e.workerRole)}</td>
|
||||
<td class="small">${d}</td>
|
||||
<td class="text-end fw-semibold">${e.hoursWorked.toFixed(2)}</td>
|
||||
<td class="small">${e.stage ? `<span class="badge bg-secondary-subtle text-secondary">${esc(e.stage)}</span>` : '<span class="text-muted">—</span>'}</td>
|
||||
@@ -2786,7 +2784,7 @@
|
||||
if (!e) return;
|
||||
document.getElementById('timeEntryModalTitle').textContent = 'Edit Time Entry';
|
||||
document.getElementById('teEntryId').value = e.id;
|
||||
document.getElementById('teWorkerId').value = e.shopWorkerId;
|
||||
document.getElementById('teWorkerId').value = e.userId ?? '';
|
||||
document.getElementById('teWorkDate').value = new Date(e.workDate).toISOString().slice(0, 10);
|
||||
document.getElementById('teHoursWorked').value = e.hoursWorked;
|
||||
document.getElementById('teStage').value = e.stage ?? '';
|
||||
@@ -2813,8 +2811,8 @@
|
||||
const tok = document.querySelector('input[name="__RequestVerificationToken"]')?.value ?? '';
|
||||
const url = id > 0 ? '/Jobs/UpdateTimeEntry' : '/Jobs/AddTimeEntry';
|
||||
const body = id > 0
|
||||
? { id, shopWorkerId: workerId, workDate, hoursWorked: hours, stage: stage || null, notes: notes || null }
|
||||
: { jobId: jid, shopWorkerId: workerId, workDate, hoursWorked: hours, stage: stage || null, notes: notes || null };
|
||||
? { id, userId: workerId, workDate, hoursWorked: hours, stage: stage || null, notes: notes || null }
|
||||
: { jobId: jid, userId: workerId, workDate, hoursWorked: hours, stage: stage || null, notes: notes || null };
|
||||
|
||||
try {
|
||||
const r = await fetch(url, {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
@model List<PowderCoating.Application.DTOs.Job.JobDailyPriorityDto>
|
||||
@using PowderCoating.Core.Entities
|
||||
|
||||
@{
|
||||
Layout = null;
|
||||
var workers = ViewBag.Workers as List<ShopWorker> ?? new();
|
||||
var workers = (ViewBag.Workers as IEnumerable<dynamic>) ?? Array.Empty<dynamic>();
|
||||
var currentWorkerId = ViewBag.CurrentWorkerId as string;
|
||||
var allStatuses = ViewBag.AllStatuses as List<JobStatusLookup> ?? new();
|
||||
var activeCount = Model.Count;
|
||||
|
||||
Reference in New Issue
Block a user