From 47d13ba9227cb41f6c72c99a28841ffeddd64b58 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Sat, 12 Oct 2024 02:36:38 +0100 Subject: [PATCH] Add support for adding 'loose' items to transactions --- Groceries.Data/Items/ItemPurchase.cs | 3 +- .../20241012012628_transaction_items_unit.sql | 35 ++++++++++ .../Transactions/TransactionItem.cs | 8 ++- Groceries/Components/PropertyTableColumn.cs | 9 ++- Groceries/Components/TablePaginator.razor | 2 +- .../NewTransactionItemModal.razor | 2 +- .../Transactions/NewTransactionItemPage.razor | 2 +- .../NewTransactionItemsPage.razor | 20 ++++-- .../Transactions/TransactionItemForm.razor | 40 +++++++---- .../Transactions/TransactionsController.cs | 16 ++--- Groceries/Transactions/TransactionsPage.razor | 2 +- Groceries/wwwroot/css/main.css | 68 ++++++++++++++++++- .../js/controllers/transaction-item-form.js | 17 ++--- 13 files changed, 178 insertions(+), 46 deletions(-) create mode 100644 Groceries.Data/Migrations/20241012012628_transaction_items_unit.sql diff --git a/Groceries.Data/Items/ItemPurchase.cs b/Groceries.Data/Items/ItemPurchase.cs index 884496e..7f61fec 100644 --- a/Groceries.Data/Items/ItemPurchase.cs +++ b/Groceries.Data/Items/ItemPurchase.cs @@ -7,7 +7,8 @@ public class ItemPurchase public DateTime CreatedAt { get; init; } public Guid StoreId { get; init; } public decimal Price { get; init; } - public int Quantity { get; init; } + public decimal Quantity { get; init; } + public string? Unit { get; init; } public bool IsLastPurchase { get; init; } public Item? Item { get; init; } diff --git a/Groceries.Data/Migrations/20241012012628_transaction_items_unit.sql b/Groceries.Data/Migrations/20241012012628_transaction_items_unit.sql new file mode 100644 index 0000000..7c2a77a --- /dev/null +++ b/Groceries.Data/Migrations/20241012012628_transaction_items_unit.sql @@ -0,0 +1,35 @@ +DROP VIEW item_purchases; +DROP VIEW transaction_totals; + +ALTER TABLE transaction_items + ALTER COLUMN quantity TYPE numeric(5, 3); + +ALTER TABLE transaction_items + ADD COLUMN IF NOT EXISTS unit text; + +CREATE VIEW item_purchases AS +SELECT + item_id, + transaction_id, + created_at, + store_id, + price, + quantity, + unit, + CASE ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY created_at DESC) + WHEN 1 THEN true + ELSE false + END AS is_last_purchase +FROM transaction_items +JOIN transactions USING (transaction_id); + +CREATE VIEW transaction_totals AS +SELECT transaction_id, sum(amount) AS total +FROM ( + SELECT transaction_id, price * quantity AS amount + FROM transaction_items + UNION ALL + SELECT transaction_id, -amount + FROM transaction_promotions +) AS transaction_amounts +GROUP BY transaction_id; diff --git a/Groceries.Data/Transactions/TransactionItem.cs b/Groceries.Data/Transactions/TransactionItem.cs index 6b2bb25..2ef3b1d 100644 --- a/Groceries.Data/Transactions/TransactionItem.cs +++ b/Groceries.Data/Transactions/TransactionItem.cs @@ -5,22 +5,24 @@ using System.Text.Json.Serialization; public class TransactionItem { [JsonConstructor] - public TransactionItem(Guid transactionId, Guid itemId, decimal price, int quantity) + public TransactionItem(Guid transactionId, Guid itemId, decimal price, decimal quantity, string? unit) { TransactionId = transactionId; ItemId = itemId; Price = price; Quantity = quantity; + Unit = unit; } - public TransactionItem(Guid itemId, decimal price, int quantity) : this(default, itemId, price, quantity) + public TransactionItem(Guid itemId, decimal price, decimal quantity, string? unit) : this(default, itemId, price, quantity, unit) { } public Guid TransactionId { get; init; } public Guid ItemId { get; set; } public decimal Price { get; set; } - public int Quantity { get; set; } + public decimal Quantity { get; set; } + public string? Unit { get; set; } public Item? Item { get; set; } diff --git a/Groceries/Components/PropertyTableColumn.cs b/Groceries/Components/PropertyTableColumn.cs index b4d8ae7..99b016e 100644 --- a/Groceries/Components/PropertyTableColumn.cs +++ b/Groceries/Components/PropertyTableColumn.cs @@ -20,6 +20,9 @@ public class PropertyTableColumn : TableColumn [Parameter] public string? Format { get; set; } + [Parameter] + public Func? CompositeFormat { get; set; } + [Parameter] public override bool Sortable { get; set; } @@ -55,7 +58,11 @@ public class PropertyTableColumn : TableColumn if (ChildContent == null) { - if (!string.IsNullOrEmpty(Format) && + if (CompositeFormat != null) + { + cellTextFunc = item => string.Format(CompositeFormat(item), compiledPropertyExpression(item)); + } + else if (!string.IsNullOrEmpty(Format) && typeof(IFormattable).IsAssignableFrom(Nullable.GetUnderlyingType(typeof(TProp)) ?? typeof(TProp))) { cellTextFunc = item => ((IFormattable?)compiledPropertyExpression(item))?.ToString(Format, null); diff --git a/Groceries/Components/TablePaginator.razor b/Groceries/Components/TablePaginator.razor index c7ffffd..8be4e8f 100644 --- a/Groceries/Components/TablePaginator.razor +++ b/Groceries/Components/TablePaginator.razor @@ -4,7 +4,7 @@ Showing @FirstItem to @LastItem of @State.TotalItemCount results -