Add ability to enter promotions when creating transaction
This commit is contained in:
parent
c0c1743d78
commit
bb2820f7df
@ -1,5 +1,6 @@
|
||||
namespace Groceries.Data;
|
||||
|
||||
using Humanizer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class AppDbContext : DbContext
|
||||
@ -81,7 +82,8 @@ public class AppDbContext : DbContext
|
||||
|
||||
entity.HasMany(e => e.Items)
|
||||
.WithMany(e => e.TransactionPromotions)
|
||||
.UsingEntity<TransactionPromotionItem>();
|
||||
.UsingEntity<TransactionPromotionItem>()
|
||||
.ToTable("transaction_promotion_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<TransactionTotal>(entity =>
|
||||
@ -95,7 +97,7 @@ public class AppDbContext : DbContext
|
||||
var idProperty = entity.FindProperty("Id");
|
||||
if (idProperty != null)
|
||||
{
|
||||
idProperty.SetColumnName($"{entity.ClrType.Name.ToLowerInvariant()}_id");
|
||||
idProperty.SetColumnName($"{entity.ClrType.Name.Underscore().ToLowerInvariant()}_id");
|
||||
idProperty.SetDefaultValueSql(string.Empty);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DbUp-PostgreSQL" Version="5.0.8" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="7.0.2" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
namespace Groceries.Data;
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public class Item
|
||||
{
|
||||
[JsonConstructor]
|
||||
public Item(Guid id, string brand, string name)
|
||||
{
|
||||
Id = id;
|
||||
|
@ -24,4 +24,6 @@ public class Transaction
|
||||
public ICollection<TransactionPromotion> Promotions { get; init; } = new List<TransactionPromotion>();
|
||||
|
||||
public Store? Store { get; init; }
|
||||
|
||||
public decimal Total => Items.Sum(item => item.Price * item.Quantity) - Promotions.Sum(promotion => promotion.Amount);
|
||||
}
|
||||
|
@ -18,9 +18,11 @@ public class TransactionItem
|
||||
}
|
||||
|
||||
public Guid TransactionId { get; init; }
|
||||
public Guid ItemId { get; init; }
|
||||
public Guid ItemId { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
|
||||
public Item? Item { get; init; }
|
||||
|
||||
public decimal Amount => Price * Quantity;
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
namespace Groceries.Data;
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public class TransactionPromotion
|
||||
{
|
||||
[JsonConstructor]
|
||||
public TransactionPromotion(Guid id, Guid transactionId, string name, decimal amount)
|
||||
{
|
||||
Id = id;
|
||||
@ -10,12 +13,16 @@ public class TransactionPromotion
|
||||
Amount = amount;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid TransactionId { get; set; }
|
||||
public string Name { get; set; } = null!;
|
||||
public TransactionPromotion(string name, decimal amount) : this(Guid.NewGuid(), default, name, amount)
|
||||
{
|
||||
}
|
||||
|
||||
public Guid Id { get; init; }
|
||||
public Guid TransactionId { get; init; }
|
||||
public string Name { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
public ICollection<Item> Items { get; init; } = new List<Item>();
|
||||
public ICollection<Item> Items { get; set; } = new List<Item>();
|
||||
|
||||
public Transaction? Transaction { get; init; }
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.9" />
|
||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
||||
</ItemGroup>
|
||||
|
32
Groceries/Transactions/EditTransactionPromotion.cshtml
Normal file
32
Groceries/Transactions/EditTransactionPromotion.cshtml
Normal file
@ -0,0 +1,32 @@
|
||||
@using Groceries.Data;
|
||||
@using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@model (Transaction Transaction, TransactionPromotion Promotion)
|
||||
@inject AppDbContext dbContext
|
||||
@{
|
||||
ViewBag.Title = "Edit Transaction Promotion";
|
||||
|
||||
var store = await dbContext.Stores
|
||||
.Where(store => store.Id == Model.Transaction.StoreId)
|
||||
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
|
||||
.SingleAsync();
|
||||
}
|
||||
|
||||
<h1>Edit Transaction Promotion</h1>
|
||||
|
||||
<div class="form-field">
|
||||
@Model.Transaction.CreatedAt.ToShortDateString() @Model.Transaction.CreatedAt.ToShortTimeString() – @store
|
||||
</div>
|
||||
|
||||
<form id="editTransactionPromotion" method="post" asp-action="EditTransactionPromotion">
|
||||
<partial name="_TransactionPromotionForm" model="Model" />
|
||||
</form>
|
||||
|
||||
<form id="deleteTransactionPromotion" method="post" asp-action="DeleteTransactionPromotion" asp-route-id="@Model.Promotion.Id"></form>
|
||||
|
||||
<div class="row">
|
||||
<button class="button button--primary" type="submit" form="editTransactionPromotion">Update</button>
|
||||
<a class="button" asp-action="NewTransactionPromotions">Cancel</a>
|
||||
<span class="row__fill"></span>
|
||||
<button class="button button--danger" type="submit" form="deleteTransactionPromotion">Remove</button>
|
||||
</div>
|
20
Groceries/Transactions/EditTransactionPromotion_Modal.cshtml
Normal file
20
Groceries/Transactions/EditTransactionPromotion_Modal.cshtml
Normal file
@ -0,0 +1,20 @@
|
||||
@using Groceries.Data
|
||||
|
||||
@model (Transaction Transaction, TransactionPromotion Promotion)
|
||||
@{
|
||||
Layout = "_Modal";
|
||||
ViewBag.Title = "Edit Transaction Promotion";
|
||||
}
|
||||
|
||||
<form class="card__content" id="editTransactionPromotion" method="post" asp-action="EditTransactionPromotion" data-action="turbo:submit-end->modal#close">
|
||||
<partial name="_TransactionPromotionForm" model="Model" />
|
||||
</form>
|
||||
|
||||
<form id="deleteTransactionPromotion" method="post" asp-action="DeleteTransactionPromotion" asp-route-id="@Model.Promotion.Id" data-action="turbo:submit-end->modal#close"></form>
|
||||
|
||||
<footer class="card__footer card__footer--shaded row">
|
||||
<button class="button button--primary" type="submit" form="editTransactionPromotion">Update</button>
|
||||
<button class="button" data-action="modal#close">Cancel</button>
|
||||
<span class="row__fill"></span>
|
||||
<button class="button button--danger" type="submit" form="deleteTransactionPromotion">Remove</button>
|
||||
</footer>
|
@ -12,10 +12,9 @@
|
||||
.SingleAsync();
|
||||
|
||||
var itemIds = Model.Items.Select(item => item.ItemId);
|
||||
var items = await dbContext.Items
|
||||
var itemNames = await dbContext.Items
|
||||
.Where(item => itemIds.Contains(item.Id))
|
||||
.Select(item => new { item.Id, Name = string.Concat(item.Brand, " ", item.Name) })
|
||||
.ToListAsync();
|
||||
.ToDictionaryAsync(item => item.Id, item => string.Concat(item.Brand, " ", item.Name));
|
||||
}
|
||||
|
||||
<h1>New Transaction</h1>
|
||||
@ -45,7 +44,7 @@
|
||||
{
|
||||
<tr>
|
||||
<td class="table__cell table__cell--compact">
|
||||
@items.Single(i => i.Id == item.ItemId).Name
|
||||
@itemNames[item.ItemId]
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact table__cell--numeric">
|
||||
@item.Price.ToString("c")
|
||||
@ -54,7 +53,7 @@
|
||||
@item.Quantity
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact table__cell--numeric">
|
||||
@((item.Price * item.Quantity).ToString("c"))
|
||||
@item.Amount.ToString("c")
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact">
|
||||
<a class="link" asp-action="EditTransactionItem" asp-route-id="@item.ItemId" data-turbo-frame="modal">Edit</a>
|
||||
@ -64,9 +63,9 @@
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="table__cell table__cell--compact table__cell--total" colspan="3">Total</td>
|
||||
<td class="table__cell table__cell--compact table__cell--total" colspan="3">Subtotal</td>
|
||||
<td class="table__cell table__cell--compact table__cell--numeric table__cell--total">
|
||||
@Model.Items.Sum(item => item.Price * item.Quantity).ToString("c")
|
||||
@Model.Items.Sum(item => item.Amount).ToString("c")
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact"></td>
|
||||
</tr>
|
||||
@ -76,7 +75,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button class="button button--primary" type="submit" @(Model.Items.Count == 0 ? "disabled" : "")>Save</button>
|
||||
<a class="button" asp-action="Index">Cancel</a>
|
||||
<button class="button button--primary" type="submit" @(Model.Items.Count == 0 ? "disabled" : "")>Next</button>
|
||||
<a class="button" asp-action="NewTransaction">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
|
26
Groceries/Transactions/NewTransactionPromotion.cshtml
Normal file
26
Groceries/Transactions/NewTransactionPromotion.cshtml
Normal file
@ -0,0 +1,26 @@
|
||||
@using Groceries.Data;
|
||||
@using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@model Transaction
|
||||
@inject AppDbContext dbContext
|
||||
@{
|
||||
ViewBag.Title = "New Transaction Promotion";
|
||||
|
||||
var store = await dbContext.Stores
|
||||
.Where(store => store.Id == Model.StoreId)
|
||||
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
|
||||
.SingleAsync();
|
||||
}
|
||||
|
||||
<h1>New Transaction Promotion</h1>
|
||||
|
||||
<div class="form-field">@Model.CreatedAt.ToShortDateString() @Model.CreatedAt.ToShortTimeString() – @store</div>
|
||||
|
||||
<form method="post" asp-action="NewTransactionPromotion">
|
||||
<partial name="_TransactionPromotionForm" model="(Model, (TransactionPromotion?)null)" />
|
||||
|
||||
<div class="row">
|
||||
<button class="button button--primary" type="submit">Add</button>
|
||||
<a class="button" asp-action="NewTransactionPromotions">Cancel</a>
|
||||
</div>
|
||||
</form>
|
16
Groceries/Transactions/NewTransactionPromotion_Modal.cshtml
Normal file
16
Groceries/Transactions/NewTransactionPromotion_Modal.cshtml
Normal file
@ -0,0 +1,16 @@
|
||||
@using Groceries.Data
|
||||
|
||||
@model Transaction
|
||||
@{
|
||||
Layout = "_Modal";
|
||||
ViewBag.Title = "New Transaction Promotion";
|
||||
}
|
||||
|
||||
<form class="card__content" id="newTransactionPromotion" method="post" asp-action="NewTransactionPromotion" data-action="turbo:submit-end->modal#close">
|
||||
<partial name="_TransactionPromotionForm" model="(Model, (TransactionPromotion?)null)" />
|
||||
</form>
|
||||
|
||||
<footer class="card__footer card__footer--shaded row">
|
||||
<button class="button button--primary" type="submit" form="newTransactionPromotion">Add</button>
|
||||
<button class="button" data-action="modal#close">Cancel</button>
|
||||
</footer>
|
74
Groceries/Transactions/NewTransactionPromotions.cshtml
Normal file
74
Groceries/Transactions/NewTransactionPromotions.cshtml
Normal file
@ -0,0 +1,74 @@
|
||||
@using Groceries.Data;
|
||||
@using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@model Transaction
|
||||
@inject AppDbContext dbContext
|
||||
@{
|
||||
ViewBag.Title = "New Transaction";
|
||||
|
||||
var store = await dbContext.Stores
|
||||
.Where(store => store.Id == Model.StoreId)
|
||||
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
|
||||
.SingleAsync();
|
||||
}
|
||||
|
||||
<h1>New Transaction</h1>
|
||||
|
||||
<div class="form-field">@Model.CreatedAt.ToShortDateString() @Model.CreatedAt.ToLongTimeString() – @store</div>
|
||||
|
||||
<form method="post" asp-action="NewTransactionPromotions">
|
||||
<div class="card form-field">
|
||||
<div class="card__header row">
|
||||
<h2 class="row__fill">Promotions</h2>
|
||||
<a class="button button--primary" asp-action="NewTransactionPromotion" autofocus data-turbo-frame="modal">
|
||||
New promotion
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card__content card__content--table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="table__header" style="width: 100%">Name</th>
|
||||
<th scope="col" class="table__header">Items</th>
|
||||
<th scope="col" class="table__header">Amount</th>
|
||||
<th scope="col" class="table__header"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var promotion in Model.Promotions)
|
||||
{
|
||||
<tr>
|
||||
<td class="table__cell table__cell--compact">
|
||||
@promotion.Name
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact table__cell--numeric">
|
||||
@promotion.Items.Sum(item => Model.Items.Single(i => i.ItemId == item.Id).Quantity)
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact table__cell--numeric">
|
||||
@(-promotion.Amount)
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact">
|
||||
<a class="link" asp-action="EditTransactionPromotion" asp-route-id="@promotion.Id" data-turbo-frame="modal">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="table__cell table__cell--compact table__cell--total" colspan="3">Total</td>
|
||||
<td class="table__cell table__cell--compact table__cell--numeric table__cell--total">
|
||||
@Model.Total.ToString("c")
|
||||
</td>
|
||||
<td class="table__cell table__cell--compact"></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button class="button button--primary" type="submit" @(Model.Items.Count == 0 ? "disabled" : "")>Save</button>
|
||||
<a class="button" asp-action="NewTransactionItems">Back</a>
|
||||
</div>
|
||||
</form>
|
@ -88,17 +88,14 @@ public class TransactionsController : Controller
|
||||
}
|
||||
|
||||
[HttpPost("new/items")]
|
||||
public async Task<IActionResult> PostNewTransactionItems()
|
||||
public IActionResult PostNewTransactionItems()
|
||||
{
|
||||
if (TempData["NewTransaction"] is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
dbContext.Transactions.Add(transaction);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
return RedirectToAction(nameof(Index), new { page = 1 });
|
||||
return RedirectToAction(nameof(NewTransactionPromotions));
|
||||
}
|
||||
|
||||
[HttpGet("new/items/new")]
|
||||
@ -186,26 +183,17 @@ public class TransactionsController : Controller
|
||||
.Select(item => item.Id)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
if (itemId == transactionItem.ItemId)
|
||||
if (itemId == default)
|
||||
{
|
||||
transactionItem.Price = price;
|
||||
transactionItem.Quantity = quantity;
|
||||
var item = new Item(brand, name);
|
||||
dbContext.Items.Add(item);
|
||||
await dbContext.SaveChangesAsync();
|
||||
itemId = item.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (itemId == default)
|
||||
{
|
||||
var item = new Item(brand, name);
|
||||
dbContext.Items.Add(item);
|
||||
await dbContext.SaveChangesAsync();
|
||||
itemId = item.Id;
|
||||
}
|
||||
|
||||
transaction.Items.Remove(transactionItem);
|
||||
|
||||
transactionItem = new TransactionItem(itemId, price, quantity);
|
||||
transaction.Items.Add(transactionItem);
|
||||
}
|
||||
transactionItem.ItemId = itemId;
|
||||
transactionItem.Price = price;
|
||||
transactionItem.Quantity = quantity;
|
||||
|
||||
TempData["NewTransaction"] = JsonSerializer.Serialize(transaction);
|
||||
|
||||
@ -234,4 +222,131 @@ public class TransactionsController : Controller
|
||||
? NoContent()
|
||||
: RedirectToAction(nameof(NewTransactionItems));
|
||||
}
|
||||
|
||||
[HttpGet("new/promotions")]
|
||||
public IActionResult NewTransactionPromotions()
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
return View(transaction);
|
||||
}
|
||||
|
||||
[HttpPost("new/promotions")]
|
||||
public async Task<IActionResult> PostNewTransactionPromotions()
|
||||
{
|
||||
if (TempData["NewTransaction"] is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
// Work around EF trying to insert items by explicitly tracking them as unchanged
|
||||
dbContext.Items.AttachRange(transaction.Promotions.SelectMany(promotion => promotion.Items));
|
||||
|
||||
dbContext.Transactions.Add(transaction);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
return RedirectToAction(nameof(Index), new { page = 1 });
|
||||
}
|
||||
|
||||
[HttpGet("new/promotions/new")]
|
||||
public IActionResult NewTransactionPromotion()
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
return Request.IsTurboFrameRequest("modal")
|
||||
? View($"{nameof(NewTransactionPromotion)}_Modal", transaction)
|
||||
: View(transaction);
|
||||
}
|
||||
|
||||
[HttpPost("new/promotions/new")]
|
||||
public IActionResult NewTransactionPromotion(string name, decimal amount, Guid[] itemIds)
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
// TODO: Handle promotion already in transaction - merge, replace, error?
|
||||
|
||||
var promotion = new TransactionPromotion(name, amount) { Items = itemIds.Select(id => new Item(id)).ToArray() };
|
||||
transaction.Promotions.Add(promotion);
|
||||
|
||||
TempData["NewTransaction"] = JsonSerializer.Serialize(transaction);
|
||||
|
||||
return Request.IsTurboFrameRequest("modal")
|
||||
? NoContent()
|
||||
: RedirectToAction(nameof(NewTransactionPromotions));
|
||||
}
|
||||
|
||||
[HttpGet("new/promotions/edit/{id}")]
|
||||
public IActionResult EditTransactionPromotion(Guid id)
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
var promotion = transaction.Promotions.SingleOrDefault(promotion => promotion.Id == id);
|
||||
if (promotion == null)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransactionPromotions));
|
||||
}
|
||||
|
||||
var model = (transaction, promotion);
|
||||
return Request.IsTurboFrameRequest("modal")
|
||||
? View($"{nameof(EditTransactionPromotion)}_Modal", model)
|
||||
: View(model);
|
||||
}
|
||||
|
||||
[HttpPost("new/promotions/edit/{id}")]
|
||||
public IActionResult EditTransactionPromotion(Guid id, string name, decimal amount, Guid[] itemIds)
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
var promotion = transaction.Promotions.SingleOrDefault(promotion => promotion.Id == id);
|
||||
if (promotion == null)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransactionPromotions));
|
||||
}
|
||||
|
||||
promotion.Name = name;
|
||||
promotion.Amount = amount;
|
||||
promotion.Items = itemIds.Select(id => new Item(id)).ToArray();
|
||||
|
||||
TempData["NewTransaction"] = JsonSerializer.Serialize(transaction);
|
||||
|
||||
return Request.IsTurboFrameRequest("modal")
|
||||
? NoContent()
|
||||
: RedirectToAction(nameof(NewTransactionPromotions));
|
||||
}
|
||||
|
||||
[HttpPost("new/promotions/delete/{id}")]
|
||||
public IActionResult DeleteTransactionPromotion(Guid id)
|
||||
{
|
||||
if (TempData.Peek("NewTransaction") is not string json || JsonSerializer.Deserialize<Transaction>(json) is not Transaction transaction)
|
||||
{
|
||||
return RedirectToAction(nameof(NewTransaction));
|
||||
}
|
||||
|
||||
var promotion = transaction.Promotions.SingleOrDefault(promotion => promotion.Id == id);
|
||||
if (promotion != null)
|
||||
{
|
||||
transaction.Promotions.Remove(promotion);
|
||||
}
|
||||
|
||||
TempData["NewTransaction"] = JsonSerializer.Serialize(transaction);
|
||||
|
||||
return Request.IsTurboFrameRequest("modal")
|
||||
? NoContent()
|
||||
: RedirectToAction(nameof(NewTransactionPromotions));
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,8 @@
|
||||
@{
|
||||
var items = await dbContext.Items
|
||||
.OrderBy(item => item.Brand)
|
||||
.ToListAsync();
|
||||
.ThenBy(item => item.Name)
|
||||
.ToArrayAsync();
|
||||
|
||||
var selectedItem = items.SingleOrDefault(item => item.Id == Model?.ItemId);
|
||||
}
|
||||
@ -25,7 +26,7 @@
|
||||
</datalist>
|
||||
|
||||
<datalist id="itemNames">
|
||||
@foreach (var item in items.OrderBy(item => item.Name))
|
||||
@foreach (var item in items)
|
||||
{
|
||||
<option value="@item.Name" data-list-filter-target="option" data-list-filter-value="@item.Brand" />
|
||||
}
|
||||
|
38
Groceries/Transactions/_TransactionPromotionForm.cshtml
Normal file
38
Groceries/Transactions/_TransactionPromotionForm.cshtml
Normal file
@ -0,0 +1,38 @@
|
||||
@using Groceries.Data
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
|
||||
@model (Transaction Transaction, TransactionPromotion? Promotion)
|
||||
@inject AppDbContext dbContext
|
||||
@{
|
||||
var selectedItemIds = Model.Promotion?.Items.Select(item => item.Id).ToArray() ?? Array.Empty<Guid>();
|
||||
|
||||
var itemIds = Model.Transaction.Items.Select(item => item.ItemId);
|
||||
var itemNames = await dbContext.Items
|
||||
.Where(item => itemIds.Contains(item.Id))
|
||||
.ToDictionaryAsync(item => item.Id, item => string.Concat(item.Brand, " ", item.Name));
|
||||
}
|
||||
|
||||
<div class="form-field">
|
||||
<label class="form-field__label" for="transactionPromotionName">Name</label>
|
||||
<div class="form-field__control input">
|
||||
<input class="input__control" id="transactionPromotionName" name="name" value="@Model.Promotion?.Name" required autofocus />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label class="form-field__label" for="transactionPromotionAmount">Amount</label>
|
||||
<div class="form-field__control input">
|
||||
@*<span class="input__inset">@CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol</span>*@
|
||||
<input class="input__control" id="transactionPromotionAmount" name="amount" value="@Model.Promotion?.Amount" type="number" min="0" step="0.01" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label class="form-field__label" for="transactionPromotionItemIds">Items</label>
|
||||
<select class="form-field__control select" id="transactionPromotionItemIds" name="itemIds" multiple required>
|
||||
@foreach (var item in Model.Transaction.Items)
|
||||
{
|
||||
<option value="@item.ItemId" selected="@selectedItemIds.Contains(item.ItemId)">@itemNames[item.ItemId]</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
@ -650,6 +650,9 @@ html:has(.modal[open]) {
|
||||
line-height: 1.25rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border: 1px solid rgb(209, 213, 219);
|
||||
}
|
||||
|
||||
.select:not([multiple]) {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.5rem 1.5rem;
|
||||
|
Loading…
x
Reference in New Issue
Block a user