From 8fa65408ed697859f141526842541887269171c5 Mon Sep 17 00:00:00 2001 From: Kukks Date: Fri, 8 May 2020 12:33:47 +0200 Subject: [PATCH 1/2] Archive Payment reqeusts closes #1588 --- BTCPayServer.Data/Data/PaymentRequestData.cs | 1 + ...0508090807_AddArchivedToPaymentRequests.cs | 30 ++++ .../ApplicationDbContextModelSnapshot.cs | 3 + .../Controllers/PaymentRequestController.cs | 128 ++++++++--------- .../ListPaymentRequestsViewModel.cs | 15 +- .../PaymentRequest/PaymentRequestService.cs | 1 + .../PaymentRequestRepository.cs | 26 +--- .../PaymentRequest/EditPaymentRequest.cshtml | 11 ++ .../PaymentRequest/GetPaymentRequests.cshtml | 131 +++++++++++------- .../MinimalPaymentRequest.cshtml | 9 +- .../PaymentRequest/ViewPaymentRequest.cshtml | 7 +- 11 files changed, 210 insertions(+), 152 deletions(-) create mode 100644 BTCPayServer.Data/Migrations/20200508090807_AddArchivedToPaymentRequests.cs diff --git a/BTCPayServer.Data/Data/PaymentRequestData.cs b/BTCPayServer.Data/Data/PaymentRequestData.cs index 0911b233b..78380f678 100644 --- a/BTCPayServer.Data/Data/PaymentRequestData.cs +++ b/BTCPayServer.Data/Data/PaymentRequestData.cs @@ -12,6 +12,7 @@ namespace BTCPayServer.Data get; set; } public string StoreDataId { get; set; } + public bool Archived { get; set; } public StoreData StoreData { get; set; } diff --git a/BTCPayServer.Data/Migrations/20200508090807_AddArchivedToPaymentRequests.cs b/BTCPayServer.Data/Migrations/20200508090807_AddArchivedToPaymentRequests.cs new file mode 100644 index 000000000..3508f7ccf --- /dev/null +++ b/BTCPayServer.Data/Migrations/20200508090807_AddArchivedToPaymentRequests.cs @@ -0,0 +1,30 @@ +using BTCPayServer.Data; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BTCPayServer.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20200508090807_AddArchivedToPaymentRequests")] + public partial class AddArchivedToPaymentRequests : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Archived", + table: "PaymentRequests", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + if (this.SupportDropColumn(ActiveProvider)) + { + migrationBuilder.DropColumn( + name: "Archived", + table: "PaymentRequests"); + } + } + } +} diff --git a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs index 5548d7f66..afa246d01 100644 --- a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -348,6 +348,9 @@ namespace BTCPayServer.Migrations b.Property("Id") .HasColumnType("TEXT"); + b.Property("Archived") + .HasColumnType("INTEGER"); + b.Property("Blob") .HasColumnType("BLOB"); diff --git a/BTCPayServer/Controllers/PaymentRequestController.cs b/BTCPayServer/Controllers/PaymentRequestController.cs index 4795b6b00..4f64bf731 100644 --- a/BTCPayServer/Controllers/PaymentRequestController.cs +++ b/BTCPayServer/Controllers/PaymentRequestController.cs @@ -70,14 +70,15 @@ namespace BTCPayServer.Controllers [HttpGet] [Route("")] [BitpayAPIConstraint(false)] - public async Task GetPaymentRequests(int skip = 0, int count = 50) + public async Task GetPaymentRequests(int skip = 0, int count = 50, bool includeArchived = false) { var result = await _PaymentRequestRepository.FindPaymentRequests(new PaymentRequestQuery() { - UserId = GetUserId(), Skip = skip, Count = count + UserId = GetUserId(), Skip = skip, Count = count, IncludeArchived = includeArchived }); return View(new ListPaymentRequestsViewModel() { + IncludeArchived = includeArchived, Skip = skip, Count = count, Total = result.Total, @@ -102,16 +103,14 @@ namespace BTCPayServer.Controllers { TempData.SetStatusMessageModel(new StatusMessageModel() { - Html = $"Error: You need to create at least one store. Create store", + Html = + $"Error: You need to create at least one store. Create store", Severity = StatusMessageModel.StatusSeverity.Error }); return RedirectToAction("GetPaymentRequests"); } - return View(new UpdatePaymentRequestViewModel(data) - { - Stores = stores - }); + return View(nameof(EditPaymentRequest), new UpdatePaymentRequestViewModel(data) {Stores = stores}); } [HttpPost] @@ -128,13 +127,18 @@ namespace BTCPayServer.Controllers return NotFound(); } + if ( data?.Archived is true && viewModel?.Archived is true) + { + ModelState.AddModelError(string.Empty, "You cannot edit an archived payment request."); + } + if (!ModelState.IsValid) { viewModel.Stores = new SelectList(await _StoreRepository.GetStoresByUserId(GetUserId()), nameof(StoreData.Id), nameof(StoreData.StoreName), data?.StoreDataId); - return View(viewModel); + return View(nameof(EditPaymentRequest),viewModel); } if (data == null) @@ -143,6 +147,7 @@ namespace BTCPayServer.Controllers } data.StoreDataId = viewModel.StoreId; + data.Archived = viewModel.Archived; var blob = data.GetBlob(); blob.Title = viewModel.Title; @@ -160,53 +165,12 @@ namespace BTCPayServer.Controllers { data.Created = DateTimeOffset.UtcNow; } + data = await _PaymentRequestRepository.CreateOrUpdatePaymentRequest(data); - _EventAggregator.Publish(new PaymentRequestUpdated() - { - Data = data, - PaymentRequestId = data.Id, - }); + _EventAggregator.Publish(new PaymentRequestUpdated() {Data = data, PaymentRequestId = data.Id,}); TempData[WellKnownTempData.SuccessMessage] = "Saved"; - return RedirectToAction("EditPaymentRequest", new {id = data.Id}); - } - - [HttpGet] - [Route("{id}/remove")] - [BitpayAPIConstraint(false)] - public async Task RemovePaymentRequestPrompt(string id) - { - var data = await _PaymentRequestRepository.FindPaymentRequest(id, GetUserId()); - if (data == null) - { - return NotFound(); - } - - var blob = data.GetBlob(); - return View("Confirm", new ConfirmModel() - { - Title = $"Remove Payment Request", - Description = $"Are you sure you want to remove access to the payment request '{blob.Title}' ?", - Action = "Delete" - }); - } - - [HttpPost] - [Route("{id}/remove")] - [BitpayAPIConstraint(false)] - public async Task RemovePaymentRequest(string id) - { - var result = await _PaymentRequestRepository.RemovePaymentRequest(id, GetUserId()); - if (result) - { - TempData[WellKnownTempData.SuccessMessage] = "Payment request successfully removed"; - return RedirectToAction("GetPaymentRequests"); - } - else - { - TempData[WellKnownTempData.ErrorMessage] = "Payment request could not be removed. Any request that has generated invoices cannot be removed."; - return RedirectToAction("GetPaymentRequests"); - } + return RedirectToAction(nameof(EditPaymentRequest), new {id = data.Id}); } [HttpGet] @@ -219,6 +183,7 @@ namespace BTCPayServer.Controllers { return NotFound(); } + result.HubPath = PaymentRequestHub.GetHubPath(this.Request); return View(result); } @@ -233,11 +198,23 @@ namespace BTCPayServer.Controllers { return BadRequest("Please provide an amount greater than 0"); } + var result = await _PaymentRequestService.GetPaymentRequest(id, GetUserId()); if (result == null) { return NotFound(); } + + if (result.Archived) + { + if (redirectToInvoice) + { + return RedirectToAction("ViewPaymentRequest", new {Id = id}); + } + + return BadRequest("Payment Request cannot be paid as it has been archived"); + } + result.HubPath = PaymentRequestHub.GetHubPath(this.Request); if (result.AmountDue <= 0) { @@ -259,10 +236,7 @@ namespace BTCPayServer.Controllers return BadRequest("Payment Request has expired"); } - var statusesAllowedToDisplay = new List() - { - InvoiceStatus.New - }; + var statusesAllowedToDisplay = new List() {InvoiceStatus.New}; var validInvoice = result.Invoices.FirstOrDefault(invoice => Enum.TryParse(invoice.Status, true, out var status) && statusesAllowedToDisplay.Contains(status)); @@ -283,7 +257,7 @@ namespace BTCPayServer.Controllers amount = result.AmountDue; - var pr = await _PaymentRequestRepository.FindPaymentRequest(id, null); + var pr = await _PaymentRequestRepository.FindPaymentRequest(id, null, cancellationToken); var blob = pr.GetBlob(); var store = pr.StoreData; try @@ -320,34 +294,33 @@ namespace BTCPayServer.Controllers public async Task CancelUnpaidPendingInvoice(string id, bool redirect = true) { var result = await _PaymentRequestService.GetPaymentRequest(id, GetUserId()); - if (result == null ) + if (result == null) { return NotFound(); } var invoice = result.Invoices.SingleOrDefault(requestInvoice => - requestInvoice.Status.Equals(InvoiceState.ToString(InvoiceStatus.New),StringComparison.InvariantCulture) && !requestInvoice.Payments.Any()); + requestInvoice.Status.Equals(InvoiceState.ToString(InvoiceStatus.New), + StringComparison.InvariantCulture) && !requestInvoice.Payments.Any()); - if (invoice == null ) + if (invoice == null) { return BadRequest("No unpaid pending invoice to cancel"); } - + await _InvoiceRepository.UpdatePaidInvoiceToInvalid(invoice.Id); - _EventAggregator.Publish(new InvoiceEvent(await _InvoiceRepository.GetInvoice(invoice.Id), 1008, InvoiceEvent.MarkedInvalid)); + _EventAggregator.Publish(new InvoiceEvent(await _InvoiceRepository.GetInvoice(invoice.Id), 1008, + InvoiceEvent.MarkedInvalid)); if (redirect) { TempData[WellKnownTempData.SuccessMessage] = "Payment cancelled"; - return RedirectToAction(nameof(ViewPaymentRequest), new - { - Id = id - }); + return RedirectToAction(nameof(ViewPaymentRequest), new {Id = id}); } return Ok("Payment cancelled"); } - + private string GetUserId() { return _UserManager.GetUserId(User); @@ -362,10 +335,27 @@ namespace BTCPayServer.Controllers { var model = (UpdatePaymentRequestViewModel)viewResult.Model; model.Id = null; + model.Archived = false; model.Title = $"Clone of {model.Title}"; - return View("EditPaymentRequest", model); - + } + + return NotFound(); + } + + [HttpGet("{id}/archive")] + public async Task TogglePaymentRequestArchival(string id) + { + var result = await EditPaymentRequest(id); + if (result is ViewResult viewResult) + { + var model = (UpdatePaymentRequestViewModel)viewResult.Model; + model.Archived = !model.Archived; + result = await EditPaymentRequest(id, model); + TempData[WellKnownTempData.SuccessMessage] = model.Archived + ? "The payment request has been archived and will no longer appear in the payment request list by default again." + : "The payment request has been unarchived and will appear in the payment request list by default."; + return result; } return NotFound(); diff --git a/BTCPayServer/Models/PaymentRequestViewModels/ListPaymentRequestsViewModel.cs b/BTCPayServer/Models/PaymentRequestViewModels/ListPaymentRequestsViewModel.cs index 42da8e44b..145a31ef9 100644 --- a/BTCPayServer/Models/PaymentRequestViewModels/ListPaymentRequestsViewModel.cs +++ b/BTCPayServer/Models/PaymentRequestViewModels/ListPaymentRequestsViewModel.cs @@ -16,6 +16,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels public List Items { get; set; } public int Total { get; set; } + public bool IncludeArchived { get; set; } } public class UpdatePaymentRequestViewModel @@ -33,7 +34,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels Id = data.Id; StoreId = data.StoreDataId; - + Archived = data.Archived; var blob = data.GetBlob(); Title = blob.Title; Amount = blob.Amount; @@ -46,6 +47,8 @@ namespace BTCPayServer.Models.PaymentRequestViewModels AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts; } + public bool Archived { get; set; } + public string Id { get; set; } [Required] public string StoreId { get; set; } @@ -81,6 +84,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels { Id = data.Id; var blob = data.GetBlob(); + Archived = data.Archived; Title = blob.Title; Amount = blob.Amount; Currency = blob.Currency; @@ -110,28 +114,20 @@ namespace BTCPayServer.Models.PaymentRequestViewModels } public bool AllowCustomPaymentAmounts { get; set; } - - public string Email { get; set; } - public string Status { get; set; } public bool IsPending { get; set; } - public decimal AmountCollected { get; set; } public decimal AmountDue { get; set; } public string AmountDueFormatted { get; set; } public decimal Amount { get; set; } public string Id { get; set; } public string Currency { get; set; } - public DateTime? ExpiryDate { get; set; } - public string Title { get; set; } public string Description { get; set; } - public string EmbeddedCSS { get; set; } public string CustomCSSLink { get; set; } - public List Invoices { get; set; } = new List(); public DateTime LastUpdated { get; set; } public CurrencyData CurrencyData { get; set; } @@ -140,6 +136,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels public bool AnyPendingInvoice { get; set; } public bool PendingInvoiceHasPayments { get; set; } public string HubPath { get; set; } + public bool Archived { get; set; } public class PaymentRequestInvoice { diff --git a/BTCPayServer/PaymentRequest/PaymentRequestService.cs b/BTCPayServer/PaymentRequest/PaymentRequestService.cs index c9aacd189..4ddda8c07 100644 --- a/BTCPayServer/PaymentRequest/PaymentRequestService.cs +++ b/BTCPayServer/PaymentRequest/PaymentRequestService.cs @@ -87,6 +87,7 @@ namespace BTCPayServer.PaymentRequest return new ViewPaymentRequestViewModel(pr) { + Archived = pr.Archived, AmountFormatted = _currencies.FormatCurrency(blob.Amount, blob.Currency), AmountCollected = paymentStats.TotalCurrency, AmountCollectedFormatted = _currencies.FormatCurrency(paymentStats.TotalCurrency, blob.Currency), diff --git a/BTCPayServer/Services/PaymentRequests/PaymentRequestRepository.cs b/BTCPayServer/Services/PaymentRequests/PaymentRequestRepository.cs index 406164257..ce5058d8b 100644 --- a/BTCPayServer/Services/PaymentRequests/PaymentRequestRepository.cs +++ b/BTCPayServer/Services/PaymentRequests/PaymentRequestRepository.cs @@ -94,6 +94,11 @@ namespace BTCPayServer.Services.PaymentRequests using (var context = _ContextFactory.CreateContext()) { var queryable = context.PaymentRequests.Include(data => data.StoreData).AsQueryable(); + + if (!query.IncludeArchived) + { + queryable = queryable.Where(data => !data.Archived); + } if (!string.IsNullOrEmpty(query.StoreId)) { queryable = queryable.Where(data => @@ -129,25 +134,6 @@ namespace BTCPayServer.Services.PaymentRequests } } - public async Task RemovePaymentRequest(string id, string userId) - { - using (var context = _ContextFactory.CreateContext()) - { - var canDelete = !(await GetInvoicesForPaymentRequest(id)).Any(); - if (!canDelete) return false; - var pr = await FindPaymentRequest(id, userId); - if (pr == null) - { - return false; - } - - context.PaymentRequests.Remove(pr); - await context.SaveChangesAsync(); - - return true; - } - } - public async Task GetInvoicesForPaymentRequest(string paymentRequestId, InvoiceQuery invoiceQuery = null) { @@ -195,7 +181,7 @@ namespace BTCPayServer.Services.PaymentRequests public class PaymentRequestQuery { public string StoreId { get; set; } - + public bool IncludeArchived { get; set; } = true; public PaymentRequestData.PaymentRequestStatus[] Status{ get; set; } public string UserId { get; set; } public int? Skip { get; set; } diff --git a/BTCPayServer/Views/PaymentRequest/EditPaymentRequest.cshtml b/BTCPayServer/Views/PaymentRequest/EditPaymentRequest.cshtml index bf9b0eab8..dfbd098ab 100644 --- a/BTCPayServer/Views/PaymentRequest/EditPaymentRequest.cshtml +++ b/BTCPayServer/Views/PaymentRequest/EditPaymentRequest.cshtml @@ -107,7 +107,18 @@ asp-controller="Invoice" asp-route-searchterm="@($"orderid:{PaymentRequestRepository.GetOrderIdForPaymentRequest(Model.Id)}")">Invoices Clone + @if (!Model.Archived) + { + Archive + + } + else + { + Unarchive + + } } + Back to list diff --git a/BTCPayServer/Views/PaymentRequest/GetPaymentRequests.cshtml b/BTCPayServer/Views/PaymentRequest/GetPaymentRequests.cshtml index 4908e990b..41488f100 100644 --- a/BTCPayServer/Views/PaymentRequest/GetPaymentRequests.cshtml +++ b/BTCPayServer/Views/PaymentRequest/GetPaymentRequests.cshtml @@ -10,7 +10,7 @@ {
- +
} @@ -23,68 +23,95 @@
- - - - - - - + + + + + + + - @foreach (var item in Model.Items) - { - - - - - - - - } + @foreach (var item in Model.Items) + { + + + + + + + + }
TitleExpiryPriceStatusActions
TitleExpiryPriceStatusActions
@item.Title@(item.ExpiryDate?.ToString("g") ?? "No Expiry")@item.Amount @item.Currency@item.Status - Edit - - - View - - - Invoices - - - Pay - - - Clone - - - Remove -
@item.Title@(item.ExpiryDate?.ToString("g") ?? "No Expiry")@item.Amount @item.Currency@item.Status + Edit + - + View + - + Invoices + - + Pay + - + Clone + - + @(item.Archived ? "Unarchive" : "Archive") +
- +
diff --git a/BTCPayServer/Views/PaymentRequest/MinimalPaymentRequest.cshtml b/BTCPayServer/Views/PaymentRequest/MinimalPaymentRequest.cshtml index 85f041819..152b7262b 100644 --- a/BTCPayServer/Views/PaymentRequest/MinimalPaymentRequest.cshtml +++ b/BTCPayServer/Views/PaymentRequest/MinimalPaymentRequest.cshtml @@ -102,7 +102,7 @@ } } } - @if (Model.IsPending) + @if (Model.IsPending && !Model.Archived) { @@ -147,6 +147,13 @@ } + }else if (Model.Archived) + { + + + + + } diff --git a/BTCPayServer/Views/PaymentRequest/ViewPaymentRequest.cshtml b/BTCPayServer/Views/PaymentRequest/ViewPaymentRequest.cshtml index 74795fbc2..198c8d473 100644 --- a/BTCPayServer/Views/PaymentRequest/ViewPaymentRequest.cshtml +++ b/BTCPayServer/Views/PaymentRequest/ViewPaymentRequest.cshtml @@ -143,7 +143,12 @@ else - + + + + + +