Add support for adding 'loose' items to transactions
All checks were successful
Docker Image CI / build (push) Successful in 3m45s
All checks were successful
Docker Image CI / build (push) Successful in 3m45s
This commit is contained in:
@ -16,5 +16,5 @@
|
||||
public required Transaction Transaction { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TransactionItem? TransactionItem { get; set; }
|
||||
public required TransactionItem TransactionItem { get; set; }
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
public required Transaction Transaction { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TransactionItem? TransactionItem { get; set; }
|
||||
public required TransactionItem TransactionItem { get; set; }
|
||||
|
||||
private string store = string.Empty;
|
||||
|
||||
|
@ -16,9 +16,19 @@
|
||||
<section class="card form-field">
|
||||
<div class="card__header row">
|
||||
<h2 class="row__fill">Items</h2>
|
||||
<a class="button button--primary" href="/transactions/new/items/new" autofocus data-turbo-frame="modal">
|
||||
New item
|
||||
</a>
|
||||
<div class="button-group dropdown">
|
||||
<a class="button button--primary" href="/transactions/new/items/new" autofocus data-turbo-frame="modal">
|
||||
New item
|
||||
</a>
|
||||
<button class="button button--primary dropdown__toggle" type="button" popovertarget="newItemMenu"></button>
|
||||
<ul class="dropdown__menu" id="newItemMenu" popover>
|
||||
<li>
|
||||
<a class="button dropdown__item" href="/transactions/new/items/new?unit=kg" data-turbo-frame="modal">
|
||||
New loose item
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card__content card__content--table">
|
||||
@ -27,8 +37,8 @@
|
||||
<TemplateTableColumn Title="Name" Fill="true" Context="item">
|
||||
@itemNames.GetValueOrDefault(item.ItemId)
|
||||
</TemplateTableColumn>
|
||||
<PropertyTableColumn Property="i => i.Price" Format="c" />
|
||||
<PropertyTableColumn Property="i => i.Quantity">
|
||||
<PropertyTableColumn Property="i => i.Price" CompositeFormat='i => i.Unit == null ? "{0:c}" : ("{0:c}/" + i.Unit)' />
|
||||
<PropertyTableColumn Property="i => i.Quantity" CompositeFormat='i => i.Unit == null ? "{0:f0}" : ("{0:f3}" + i.Unit)'>
|
||||
<HeaderContent>
|
||||
<abbr title="Quantity">Qty</abbr>
|
||||
</HeaderContent>
|
||||
|
@ -36,34 +36,41 @@
|
||||
<datalist id="itemNames">
|
||||
@foreach (var item in items)
|
||||
{
|
||||
<option value="@item.Name" data-transaction-item-form-target="option" data-brand="@item.Brand" data-price="@item.Price" data-quantity="@item.Quantity" />
|
||||
<option value="@item.Name" data-transaction-item-form-target="option" data-brand="@item.Brand" data-price="@item.Price" data-quantity="@(unit == null ? (int?)item.Quantity : item.Quantity)" />
|
||||
}
|
||||
</datalist>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-field">
|
||||
<label class="form-field__label" for="transactionItemPrice">Price</label>
|
||||
<label class="form-field__label" for="transactionItemPrice">
|
||||
Price @if (unit != null) { <text>(per @unit)</text> }
|
||||
</label>
|
||||
<div class="form-field__control input">
|
||||
@*<span class="input__inset">@CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol</span>*@
|
||||
<input class="input__control" id="transactionItemPrice" name="price" value="@price" type="number" min="0" step="0.01" required data-transaction-item-form-target="price" />
|
||||
<input class="input__control" id="transactionItemPrice" name="price" value="@price" type="number" min="0" step="0.01" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label class="form-field__label" for="transactionItemQuantity">Quantity</label>
|
||||
<label class="form-field__label" for="transactionItemQuantity">
|
||||
Quantity @if (unit != null) { <text>(@unit)</text> }
|
||||
</label>
|
||||
<div class="form-field__control input">
|
||||
<input class="input__control" id="transactionItemQuantity" name="quantity" value="@quantity" type="number" min="1" required data-transaction-item-form-target="quantity" />
|
||||
@{ var step = unit == null ? "1" : "0.001"; }
|
||||
<input class="input__control" id="transactionItemQuantity" name="quantity" value="@quantity" type="number" min="@step" step="@step" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="unit" value="@unit" />
|
||||
</div>
|
||||
|
||||
@ChildContent
|
||||
</form>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public TransactionItem? TransactionItem { get; set; }
|
||||
[Parameter, EditorRequired]
|
||||
public required TransactionItem TransactionItem { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
@ -71,7 +78,7 @@
|
||||
[Parameter(CaptureUnmatchedValues = true)]
|
||||
public Dictionary<string, object>? AdditionalAttributes { get; set; }
|
||||
|
||||
private record ItemModel(Guid Id, string Brand, string Name, decimal? Price, int? Quantity);
|
||||
private record ItemModel(Guid Id, string Brand, string Name, decimal? Price, decimal? Quantity);
|
||||
|
||||
private ItemBarcode? barcode;
|
||||
|
||||
@ -79,11 +86,12 @@
|
||||
private ItemModel? selectedItem;
|
||||
|
||||
private decimal? price;
|
||||
private int quantity;
|
||||
private decimal? quantity;
|
||||
private string? unit;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
barcode = TransactionItem?.Item?.Barcodes.FirstOrDefault();
|
||||
barcode = TransactionItem.Item?.Barcodes.FirstOrDefault();
|
||||
|
||||
items = await DbContext.Items
|
||||
.OrderBy(item => item.Brand)
|
||||
@ -103,9 +111,15 @@
|
||||
lastPurchase != null ? lastPurchase.Quantity : null))
|
||||
.ToArrayAsync();
|
||||
|
||||
selectedItem = items.SingleOrDefault(item => item.Id == TransactionItem?.ItemId);
|
||||
selectedItem = items.SingleOrDefault(item => item.Id == TransactionItem.ItemId);
|
||||
|
||||
price = TransactionItem?.Price >= 0 ? TransactionItem.Price : selectedItem?.Price;
|
||||
quantity = TransactionItem?.Quantity >= 1 ? TransactionItem.Quantity : (selectedItem?.Quantity ?? 1);
|
||||
price = TransactionItem.Price >= 0 ? TransactionItem.Price : selectedItem?.Price;
|
||||
quantity = TransactionItem.Quantity >= 0 ? TransactionItem.Quantity : selectedItem?.Quantity;
|
||||
unit = TransactionItem.Unit;
|
||||
|
||||
if (unit == null)
|
||||
{
|
||||
quantity ??= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,17 +60,17 @@ public class TransactionsController : Controller
|
||||
}
|
||||
|
||||
[HttpGet("new/items/new")]
|
||||
public async Task<IResult> NewTransactionItem(long? barcodeData, string? barcodeFormat)
|
||||
public async Task<IResult> NewTransactionItem(string? unit, long? barcodeData, string? barcodeFormat)
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return Results.LocalRedirect("/transactions/new");
|
||||
}
|
||||
|
||||
TransactionItem? transactionItem = null;
|
||||
Item? item = null;
|
||||
if (barcodeData != null && barcodeFormat != null)
|
||||
{
|
||||
var item = await dbContext.Items
|
||||
item = await dbContext.Items
|
||||
.Where(item => item.Barcodes.Any(barcode => barcode.BarcodeData == barcodeData))
|
||||
.OrderByDescending(item => item.UpdatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
@ -86,11 +86,11 @@ public class TransactionsController : Controller
|
||||
dbContext.Update(barcode);
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// TODO: Fix `MinValue` hack - view models?
|
||||
transactionItem = new TransactionItem(item.Id, decimal.MinValue, int.MinValue) { Item = item };
|
||||
}
|
||||
|
||||
// TODO: Fix `MinValue` hack - view models?
|
||||
var transactionItem = new TransactionItem(item?.Id ?? default, decimal.MinValue, decimal.MinValue, unit) { Item = item };
|
||||
|
||||
var parameters = new { Transaction = transaction, TransactionItem = transactionItem };
|
||||
return Request.IsTurboFrameRequest("modal")
|
||||
? new RazorComponentResult<NewTransactionItemModal>(parameters)
|
||||
@ -98,7 +98,7 @@ public class TransactionsController : Controller
|
||||
}
|
||||
|
||||
[HttpPost("new/items/new")]
|
||||
public async Task<IResult> NewTransactionItem(string brand, string name, decimal price, int quantity, long? barcodeData, string? barcodeFormat)
|
||||
public async Task<IResult> NewTransactionItem(string brand, string name, decimal price, decimal quantity, string? unit, long? barcodeData, string? barcodeFormat)
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
@ -127,7 +127,7 @@ public class TransactionsController : Controller
|
||||
|
||||
// TODO: Handle item already in transaction - merge, replace, error?
|
||||
|
||||
var transactionItem = new TransactionItem(item.Id, price, quantity) { Item = item };
|
||||
var transactionItem = new TransactionItem(item.Id, price, quantity, unit) { Item = item };
|
||||
transaction.Items.Add(transactionItem);
|
||||
|
||||
TempData["NewTransaction"] = JsonSerializer.Serialize(transaction);
|
||||
|
@ -51,7 +51,7 @@
|
||||
CreatedAt = transaction.CreatedAt,
|
||||
Store = string.Concat(transaction.Store!.Retailer!.Name, " ", transaction.Store.Name),
|
||||
TotalAmount = transactionTotal.Total,
|
||||
TotalItems = transaction.Items.Sum(item => item.Quantity),
|
||||
TotalItems = transaction.Items.Sum(item => item.Unit == null ? (int)item.Quantity : 1),
|
||||
})
|
||||
.OrderByDescending(transaction => transaction.CreatedAt);
|
||||
}
|
||||
|
Reference in New Issue
Block a user