fix: make stock item length editable and drop vendor UI from Stock Edit page
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
@page "/stock/{Id:int}"
|
@page "/stock/{Id:int}"
|
||||||
@inject StockItemService StockItemService
|
@inject StockItemService StockItemService
|
||||||
@inject MaterialService MaterialService
|
@inject MaterialService MaterialService
|
||||||
@inject SupplierService SupplierService
|
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@using CutList.Core.Formatting
|
@using CutList.Core.Formatting
|
||||||
|
|
||||||
@@ -39,14 +38,7 @@ else
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Length</label>
|
<label class="form-label">Length</label>
|
||||||
@if (IsNew)
|
|
||||||
{
|
|
||||||
<LengthInput @bind-Value="stockItem.LengthInches" />
|
<LengthInput @bind-Value="stockItem.LengthInches" />
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input type="text" class="form-control" value="@ArchUnits.FormatFromInches((double)stockItem.LengthInches)" readonly />
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -82,7 +74,7 @@ else
|
|||||||
@if (!IsNew)
|
@if (!IsNew)
|
||||||
{
|
{
|
||||||
<div class="col-lg-6 mb-4">
|
<div class="col-lg-6 mb-4">
|
||||||
<div class="card mb-4">
|
<div class="card">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h5 class="mb-0">
|
<h5 class="mb-0">
|
||||||
Inventory
|
Inventory
|
||||||
@@ -112,23 +104,6 @@ else
|
|||||||
<label class="form-label">Notes</label>
|
<label class="form-label">Notes</label>
|
||||||
<InputText class="form-control" @bind-Value="stockNotes" />
|
<InputText class="form-control" @bind-Value="stockNotes" />
|
||||||
</div>
|
</div>
|
||||||
@if (stockTransactionType == "add")
|
|
||||||
{
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">Supplier (optional)</label>
|
|
||||||
<select class="form-select" @bind="stockSupplierId">
|
|
||||||
<option value="0">-- Select Supplier --</option>
|
|
||||||
@foreach (var supplier in suppliers)
|
|
||||||
{
|
|
||||||
<option value="@supplier.Id">@supplier.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">Unit Price (optional)</label>
|
|
||||||
<input type="number" class="form-control" @bind="stockUnitPrice" step="0.01" min="0" placeholder="0.00" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
@if (!string.IsNullOrEmpty(stockFormErrorMessage))
|
@if (!string.IsNullOrEmpty(stockFormErrorMessage))
|
||||||
{
|
{
|
||||||
@@ -159,8 +134,6 @@ else
|
|||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Qty</th>
|
<th>Qty</th>
|
||||||
<th>Supplier</th>
|
|
||||||
<th>Price</th>
|
|
||||||
<th>Notes</th>
|
<th>Notes</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -175,8 +148,6 @@ else
|
|||||||
<td class="@(txn.Quantity >= 0 ? "text-success" : "text-danger")">
|
<td class="@(txn.Quantity >= 0 ? "text-success" : "text-danger")">
|
||||||
@(txn.Quantity >= 0 ? "+" : "")@txn.Quantity
|
@(txn.Quantity >= 0 ? "+" : "")@txn.Quantity
|
||||||
</td>
|
</td>
|
||||||
<td>@(txn.Supplier?.Name ?? "-")</td>
|
|
||||||
<td>@(txn.UnitPrice.HasValue ? txn.UnitPrice.Value.ToString("C") : "-")</td>
|
|
||||||
<td>@(txn.Notes ?? "-")</td>
|
<td>@(txn.Notes ?? "-")</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@@ -185,145 +156,35 @@ else
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
|
||||||
<h5 class="mb-0">Supplier Offerings</h5>
|
|
||||||
<button class="btn btn-sm btn-primary" @onclick="ShowAddOfferingForm">Add Offering</button>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
@if (showOfferingForm)
|
|
||||||
{
|
|
||||||
<div class="border rounded p-3 mb-3 bg-light">
|
|
||||||
<h6>@(editingOffering == null ? "Add Offering" : "Edit Offering")</h6>
|
|
||||||
<div class="row g-2">
|
|
||||||
<div class="col-12">
|
|
||||||
<label class="form-label">Supplier</label>
|
|
||||||
<select class="form-select" @bind="newOffering.SupplierId">
|
|
||||||
<option value="0">-- Select Supplier --</option>
|
|
||||||
@foreach (var supplier in suppliers)
|
|
||||||
{
|
|
||||||
<option value="@supplier.Id">@supplier.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">Part Number</label>
|
|
||||||
<InputText class="form-control" @bind-Value="newOffering.PartNumber" />
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">Price</label>
|
|
||||||
<InputNumber class="form-control" @bind-Value="newOffering.Price" placeholder="0.00" />
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<label class="form-label">Supplier Description</label>
|
|
||||||
<InputText class="form-control" @bind-Value="newOffering.SupplierDescription" />
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<label class="form-label">Notes</label>
|
|
||||||
<InputText class="form-control" @bind-Value="newOffering.Notes" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if (!string.IsNullOrEmpty(offeringErrorMessage))
|
|
||||||
{
|
|
||||||
<div class="alert alert-danger mt-2 mb-0">@offeringErrorMessage</div>
|
|
||||||
}
|
|
||||||
<div class="mt-3 d-flex gap-2">
|
|
||||||
<button class="btn btn-primary btn-sm" @onclick="SaveOfferingAsync" disabled="@savingOffering">
|
|
||||||
@if (savingOffering)
|
|
||||||
{
|
|
||||||
<span class="spinner-border spinner-border-sm me-1"></span>
|
|
||||||
}
|
|
||||||
@(editingOffering == null ? "Add" : "Save")
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-outline-secondary btn-sm" @onclick="CancelOfferingForm">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (offerings.Count == 0)
|
|
||||||
{
|
|
||||||
<p class="text-muted">No supplier offerings configured yet.</p>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<table class="table table-sm">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Supplier</th>
|
|
||||||
<th>Part #</th>
|
|
||||||
<th>Price</th>
|
|
||||||
<th style="width: 100px;">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var offering in offerings)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@offering.Supplier.Name</td>
|
|
||||||
<td>@(offering.PartNumber ?? "-")</td>
|
|
||||||
<td>@(offering.Price.HasValue ? offering.Price.Value.ToString("C") : "-")</td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditOffering(offering)" title="Edit"><i class="bi bi-pencil"></i></button>
|
|
||||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDeleteOffering(offering)" title="Delete"><i class="bi bi-trash"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<ConfirmDialog @ref="deleteOfferingDialog"
|
|
||||||
Title="Delete Offering"
|
|
||||||
Message="@deleteOfferingMessage"
|
|
||||||
ConfirmText="Delete"
|
|
||||||
OnConfirm="DeleteOfferingConfirmed" />
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int? Id { get; set; }
|
public int? Id { get; set; }
|
||||||
|
|
||||||
private StockItem stockItem = new();
|
private StockItem stockItem = new();
|
||||||
private List<Material> materials = new();
|
private List<Material> materials = new();
|
||||||
private List<Supplier> suppliers = new();
|
|
||||||
private List<SupplierOffering> offerings = new();
|
|
||||||
private List<StockTransaction> transactions = new();
|
private List<StockTransaction> transactions = new();
|
||||||
private bool loading = true;
|
private bool loading = true;
|
||||||
private bool saving;
|
private bool saving;
|
||||||
private bool savingOffering;
|
|
||||||
private string? errorMessage;
|
private string? errorMessage;
|
||||||
private string? offeringErrorMessage;
|
|
||||||
|
|
||||||
private bool showOfferingForm;
|
|
||||||
private SupplierOffering newOffering = new();
|
|
||||||
private SupplierOffering? editingOffering;
|
|
||||||
|
|
||||||
// Stock transaction form
|
// Stock transaction form
|
||||||
private bool showStockForm;
|
private bool showStockForm;
|
||||||
private bool savingStockTransaction;
|
private bool savingStockTransaction;
|
||||||
private string stockTransactionType = "add";
|
private string stockTransactionType = "add";
|
||||||
private int stockQuantity;
|
private int stockQuantity;
|
||||||
private int stockSupplierId;
|
|
||||||
private decimal? stockUnitPrice;
|
|
||||||
private string? stockNotes;
|
private string? stockNotes;
|
||||||
private string? stockFormErrorMessage;
|
private string? stockFormErrorMessage;
|
||||||
|
|
||||||
private ConfirmDialog deleteOfferingDialog = null!;
|
|
||||||
private SupplierOffering? offeringToDelete;
|
|
||||||
private string deleteOfferingMessage = "";
|
|
||||||
|
|
||||||
private bool IsNew => !Id.HasValue;
|
private bool IsNew => !Id.HasValue;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
materials = await MaterialService.GetAllAsync();
|
materials = await MaterialService.GetAllAsync();
|
||||||
suppliers = await SupplierService.GetAllAsync();
|
|
||||||
|
|
||||||
if (Id.HasValue)
|
if (Id.HasValue)
|
||||||
{
|
{
|
||||||
@@ -334,7 +195,6 @@ else
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stockItem = existing;
|
stockItem = existing;
|
||||||
offerings = existing.SupplierOfferings.Where(o => o.IsActive).ToList();
|
|
||||||
transactions = await StockItemService.GetTransactionHistoryAsync(Id.Value, 20);
|
transactions = await StockItemService.GetTransactionHistoryAsync(Id.Value, 20);
|
||||||
}
|
}
|
||||||
loading = false;
|
loading = false;
|
||||||
@@ -354,8 +214,6 @@ else
|
|||||||
{
|
{
|
||||||
stockTransactionType = "add";
|
stockTransactionType = "add";
|
||||||
stockQuantity = 0;
|
stockQuantity = 0;
|
||||||
stockSupplierId = 0;
|
|
||||||
stockUnitPrice = null;
|
|
||||||
stockNotes = null;
|
stockNotes = null;
|
||||||
stockFormErrorMessage = null;
|
stockFormErrorMessage = null;
|
||||||
showStockForm = true;
|
showStockForm = true;
|
||||||
@@ -389,12 +247,7 @@ else
|
|||||||
switch (stockTransactionType)
|
switch (stockTransactionType)
|
||||||
{
|
{
|
||||||
case "add":
|
case "add":
|
||||||
await StockItemService.AddStockAsync(
|
await StockItemService.AddStockAsync(Id!.Value, stockQuantity, stockNotes);
|
||||||
Id!.Value,
|
|
||||||
stockQuantity,
|
|
||||||
stockSupplierId > 0 ? stockSupplierId : null,
|
|
||||||
stockUnitPrice,
|
|
||||||
stockNotes);
|
|
||||||
break;
|
break;
|
||||||
case "adjust":
|
case "adjust":
|
||||||
await StockItemService.AdjustStockAsync(Id!.Value, stockQuantity, stockNotes);
|
await StockItemService.AdjustStockAsync(Id!.Value, stockQuantity, stockNotes);
|
||||||
@@ -464,108 +317,4 @@ else
|
|||||||
saving = false;
|
saving = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowAddOfferingForm()
|
|
||||||
{
|
|
||||||
editingOffering = null;
|
|
||||||
newOffering = new SupplierOffering { StockItemId = Id!.Value };
|
|
||||||
showOfferingForm = true;
|
|
||||||
offeringErrorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EditOffering(SupplierOffering offering)
|
|
||||||
{
|
|
||||||
editingOffering = offering;
|
|
||||||
newOffering = new SupplierOffering
|
|
||||||
{
|
|
||||||
Id = offering.Id,
|
|
||||||
StockItemId = offering.StockItemId,
|
|
||||||
SupplierId = offering.SupplierId,
|
|
||||||
PartNumber = offering.PartNumber,
|
|
||||||
SupplierDescription = offering.SupplierDescription,
|
|
||||||
Price = offering.Price,
|
|
||||||
Notes = offering.Notes
|
|
||||||
};
|
|
||||||
showOfferingForm = true;
|
|
||||||
offeringErrorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CancelOfferingForm()
|
|
||||||
{
|
|
||||||
showOfferingForm = false;
|
|
||||||
editingOffering = null;
|
|
||||||
offeringErrorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveOfferingAsync()
|
|
||||||
{
|
|
||||||
offeringErrorMessage = null;
|
|
||||||
savingOffering = true;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (newOffering.SupplierId == 0)
|
|
||||||
{
|
|
||||||
offeringErrorMessage = "Please select a supplier";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var exists = await SupplierService.OfferingExistsAsync(
|
|
||||||
newOffering.SupplierId,
|
|
||||||
newOffering.StockItemId,
|
|
||||||
editingOffering?.Id);
|
|
||||||
|
|
||||||
if (exists)
|
|
||||||
{
|
|
||||||
offeringErrorMessage = "This supplier already has an offering for this stock item";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editingOffering == null)
|
|
||||||
{
|
|
||||||
await SupplierService.AddOfferingAsync(newOffering);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await SupplierService.UpdateOfferingAsync(newOffering);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh the stock item to get updated offerings
|
|
||||||
var updated = await StockItemService.GetByIdAsync(Id!.Value);
|
|
||||||
if (updated != null)
|
|
||||||
{
|
|
||||||
stockItem = updated;
|
|
||||||
offerings = updated.SupplierOfferings.Where(o => o.IsActive).ToList();
|
|
||||||
}
|
|
||||||
showOfferingForm = false;
|
|
||||||
editingOffering = null;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
savingOffering = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConfirmDeleteOffering(SupplierOffering offering)
|
|
||||||
{
|
|
||||||
offeringToDelete = offering;
|
|
||||||
deleteOfferingMessage = $"Are you sure you want to delete the offering from {offering.Supplier.Name}?";
|
|
||||||
deleteOfferingDialog.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteOfferingConfirmed()
|
|
||||||
{
|
|
||||||
if (offeringToDelete != null)
|
|
||||||
{
|
|
||||||
await SupplierService.DeleteOfferingAsync(offeringToDelete.Id);
|
|
||||||
|
|
||||||
// Refresh the stock item to get updated offerings
|
|
||||||
var updated = await StockItemService.GetByIdAsync(Id!.Value);
|
|
||||||
if (updated != null)
|
|
||||||
{
|
|
||||||
stockItem = updated;
|
|
||||||
offerings = updated.SupplierOfferings.Where(o => o.IsActive).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user