Complete Job modal: ask powder usage once per color, not per item/coat
The modal was showing one row per coat per item, so a job with 5 items each with 2 coats of the same powder produced 10 identical input rows. Now groups by unique InventoryItemId and shows one row per powder color for the whole job. The controller distributes the entered total across coats proportionally by their estimated PowderToOrder so per-coat reporting data is preserved. A single inventory transaction is created per powder (net of any pre-logged scan credit). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,21 @@
|
||||
@{
|
||||
var emailDefault = ViewBag.EmailDefaultOnComplete == true;
|
||||
var preLoggedPowder = ViewBag.PreLoggedPowder as Dictionary<int, decimal> ?? new Dictionary<int, decimal>();
|
||||
// Track remaining credit per InventoryItemId as we allocate it across coat rows
|
||||
var remainingCredit = preLoggedPowder.ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||
|
||||
// Group all coats by inventory item so we ask once per powder color, not once per item/coat
|
||||
var powderGroups = (Model.Items ?? new List<PowderCoating.Application.DTOs.Job.JobItemDto>())
|
||||
.SelectMany(i => i.Coats ?? new List<PowderCoating.Application.DTOs.Job.JobItemCoatDto>())
|
||||
.Where(c => c.InventoryItemId.HasValue)
|
||||
.GroupBy(c => c.InventoryItemId!.Value)
|
||||
.Select(g => new {
|
||||
InventoryItemId = g.Key,
|
||||
ColorName = g.First().ColorName,
|
||||
ColorCode = g.First().ColorCode,
|
||||
TotalEstimatedLbs = g.Sum(c => c.PowderToOrder ?? 0m),
|
||||
PreLogged = preLoggedPowder.GetValueOrDefault(g.Key, 0m)
|
||||
})
|
||||
.OrderBy(g => g.ColorName)
|
||||
.ToList();
|
||||
}
|
||||
<div class="modal fade" id="completeJobModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
@@ -27,102 +40,59 @@
|
||||
<div class="form-text">Enter the total time in hours (e.g., 2.5 for 2 hours 30 minutes)</div>
|
||||
</div>
|
||||
|
||||
@if (Model.Items != null && Model.Items.Any())
|
||||
@if (powderGroups.Any())
|
||||
{
|
||||
<div class="mb-3">
|
||||
<h6 class="fw-semibold mb-3">
|
||||
<h6 class="fw-semibold mb-1">
|
||||
<i class="bi bi-palette me-1 text-primary"></i>Actual Powder Usage
|
||||
</h6>
|
||||
<p class="text-muted small mb-3">Enter total lbs used per powder color for the entire job.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Coat</th>
|
||||
<th>Color</th>
|
||||
<th>Color / Powder</th>
|
||||
<th class="text-end">Estimated (lbs)</th>
|
||||
<th>Actual (lbs)</th>
|
||||
<th style="width:150px">Actual Used (lbs)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
var coatIndex = 0;
|
||||
}
|
||||
@foreach (var item in Model.Items)
|
||||
@for (int i = 0; i < powderGroups.Count; i++)
|
||||
{
|
||||
if (item.Coats != null && item.Coats.Any())
|
||||
{
|
||||
foreach (var coat in item.Coats.OrderBy(c => c.Sequence))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<small>@item.Description</small>
|
||||
@if (item.Quantity > 1)
|
||||
{
|
||||
<span class="badge bg-secondary ms-1">×@item.Quantity</span>
|
||||
}
|
||||
</td>
|
||||
<td><span class="badge bg-secondary">@coat.CoatName</span></td>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(coat.ColorName))
|
||||
{
|
||||
<small>
|
||||
@coat.ColorName
|
||||
@if (!string.IsNullOrEmpty(coat.ColorCode))
|
||||
{
|
||||
<span class="text-muted">(@coat.ColorCode)</span>
|
||||
}
|
||||
</small>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<small class="text-muted">@((coat.PowderToOrder ?? 0).ToString("0.##"))</small>
|
||||
</td>
|
||||
<td>
|
||||
@{
|
||||
decimal preFilledLbs = 0m;
|
||||
if (coat.InventoryItemId.HasValue && remainingCredit.TryGetValue(coat.InventoryItemId.Value, out var availCredit) && availCredit > 0)
|
||||
{
|
||||
preFilledLbs = availCredit;
|
||||
remainingCredit[coat.InventoryItemId.Value] = 0m;
|
||||
}
|
||||
}
|
||||
<input type="hidden" name="CoatUsages[@coatIndex].JobItemCoatId" value="@coat.Id" />
|
||||
<input type="number"
|
||||
class="form-control form-control-sm"
|
||||
name="CoatUsages[@coatIndex].ActualPowderUsedLbs"
|
||||
step="0.01" min="0" placeholder="0.00"
|
||||
value="@(preFilledLbs > 0 ? preFilledLbs.ToString("0.##") : "")"
|
||||
style="max-width: 120px;">
|
||||
@if (preFilledLbs > 0)
|
||||
{
|
||||
<small class="text-success d-block mt-1">
|
||||
<i class="bi bi-check-circle me-1"></i>Already logged — inventory adjusted
|
||||
</small>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
coatIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<tr class="table-secondary">
|
||||
<td colspan="5">
|
||||
<small class="text-muted fst-italic">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
@item.Description — No coat information available (legacy job item)
|
||||
var pg = powderGroups[i];
|
||||
<tr>
|
||||
<td>
|
||||
<span class="fw-semibold">@pg.ColorName</span>
|
||||
@if (!string.IsNullOrEmpty(pg.ColorCode))
|
||||
{
|
||||
<small class="text-muted ms-1">(@pg.ColorCode)</small>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end text-muted small align-middle">
|
||||
@pg.TotalEstimatedLbs.ToString("0.##")
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" name="PowderUsages[@i].InventoryItemId" value="@pg.InventoryItemId" />
|
||||
<input type="number"
|
||||
class="form-control form-control-sm"
|
||||
name="PowderUsages[@i].ActualPowderUsedLbs"
|
||||
step="0.01" min="0" placeholder="0.00"
|
||||
value="@(pg.PreLogged > 0 ? pg.PreLogged.ToString("0.##") : "")">
|
||||
@if (pg.PreLogged > 0)
|
||||
{
|
||||
<small class="text-success d-block mt-1">
|
||||
<i class="bi bi-check-circle me-1"></i>@pg.PreLogged.ToString("0.##") lbs already logged
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="alert alert-info alert-permanent mb-0">
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
<small>Pre-filled values were already logged via scan — inventory is already adjusted for those. You can edit the amount; only the difference will be applied to inventory.</small>
|
||||
<small>Pre-filled values were already logged via scan — inventory is already adjusted for those. You can edit the amount; only the difference will be applied.</small>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user