mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 18:11:36 +01:00
commit
9cc9a16bb9
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -79,6 +79,8 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool Archived { get; set; }
|
||||
public List<PendingInvoiceData> PendingInvoices { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20200507092343_AddArchivedToInvoice")]
|
||||
public class AddArchivedToInvoice : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Archived",
|
||||
table: "Invoices",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Archived",
|
||||
table: "Invoices");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -190,6 +190,9 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Archived")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
|
@ -240,10 +240,26 @@ namespace BTCPayServer.Tests
|
||||
var storeUrl = s.Driver.Url;
|
||||
s.ClickOnAllSideMenus();
|
||||
s.GoToInvoices();
|
||||
s.CreateInvoice(store);
|
||||
var invoiceId = s.CreateInvoice(store);
|
||||
s.AssertHappyMessage();
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
var invoiceUrl = s.Driver.Url;
|
||||
|
||||
//let's test archiving an invoice
|
||||
Assert.DoesNotContain("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
|
||||
s.Driver.FindElement(By.Id("btn-archive-toggle")).Click();
|
||||
s.AssertHappyMessage();
|
||||
Assert.Contains("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
|
||||
//check that it no longer appears in list
|
||||
s.GoToInvoices();
|
||||
Assert.DoesNotContain(invoiceId, s.Driver.PageSource);
|
||||
//ok, let's unarchive and see that it shows again
|
||||
s.Driver.Navigate().GoToUrl(invoiceUrl);
|
||||
s.Driver.FindElement(By.Id("btn-archive-toggle")).Click();
|
||||
s.AssertHappyMessage();
|
||||
Assert.DoesNotContain("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
|
||||
s.GoToInvoices();
|
||||
Assert.Contains(invoiceId, s.Driver.PageSource);
|
||||
|
||||
// When logout we should not be able to access store and invoice details
|
||||
s.Driver.FindElement(By.Id("Logout")).Click();
|
||||
|
@ -41,7 +41,8 @@ namespace BTCPayServer.Controllers
|
||||
InvoiceId = new[] {invoiceId},
|
||||
UserId = GetUserId(),
|
||||
IncludeAddresses = true,
|
||||
IncludeEvents = true
|
||||
IncludeEvents = true,
|
||||
IncludeArchived = true,
|
||||
})).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
return NotFound();
|
||||
@ -71,7 +72,8 @@ namespace BTCPayServer.Controllers
|
||||
ProductInformation = invoice.ProductInformation,
|
||||
StatusException = invoice.ExceptionStatus,
|
||||
Events = invoice.Events,
|
||||
PosData = PosDataParser.ParsePosData(invoice.PosData)
|
||||
PosData = PosDataParser.ParsePosData(invoice.PosData),
|
||||
Archived = invoice.Archived
|
||||
};
|
||||
|
||||
model.Addresses = invoice.HistoricalAddresses.Select(h =>
|
||||
@ -91,6 +93,7 @@ namespace BTCPayServer.Controllers
|
||||
private InvoiceDetailsModel InvoicePopulatePayments(InvoiceEntity invoice)
|
||||
{
|
||||
var model = new InvoiceDetailsModel();
|
||||
model.Archived = invoice.Archived;
|
||||
model.Payments = invoice.GetPayments();
|
||||
foreach (var data in invoice.GetPaymentMethods())
|
||||
{
|
||||
@ -111,6 +114,26 @@ namespace BTCPayServer.Controllers
|
||||
return model;
|
||||
}
|
||||
|
||||
[HttpPost("invoices/{invoiceId}/archive")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> ToggleArchive(string invoiceId)
|
||||
{
|
||||
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
InvoiceId = new[] {invoiceId}, UserId = GetUserId(), IncludeAddresses = true, IncludeEvents = true, IncludeArchived = true,
|
||||
})).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
return NotFound();
|
||||
await _InvoiceRepository.ToggleInvoiceArchival(invoiceId, !invoice.Archived);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = invoice.Archived ? "The invoice has been unarchived and will appear in the invoice list by default again." : "The invoice has been archived and will no longer appear in the invoice list by default."
|
||||
});
|
||||
return RedirectToAction(nameof(invoice), new {invoiceId});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("i/{invoiceId}")]
|
||||
[Route("i/{invoiceId}/{paymentMethodId}")]
|
||||
@ -437,7 +460,7 @@ namespace BTCPayServer.Controllers
|
||||
AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.ProductInformation.Price, invoice.ProductInformation.Currency),
|
||||
CanMarkInvalid = state.CanMarkInvalid(),
|
||||
CanMarkComplete = state.CanMarkComplete(),
|
||||
Details = InvoicePopulatePayments(invoice)
|
||||
Details = InvoicePopulatePayments(invoice),
|
||||
});
|
||||
}
|
||||
model.Total = await counting;
|
||||
@ -452,6 +475,7 @@ namespace BTCPayServer.Controllers
|
||||
TextSearch = fs.TextSearch,
|
||||
UserId = GetUserId(),
|
||||
Unusual = fs.GetFilterBool("unusual"),
|
||||
IncludeArchived = fs.GetFilterBool("includearchived") ?? false,
|
||||
Status = fs.GetFilterArray("status"),
|
||||
ExceptionStatus = fs.GetFilterArray("exceptionstatus"),
|
||||
StoreId = fs.GetFilterArray("storeid"),
|
||||
|
@ -127,5 +127,6 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
public string NotificationEmail { get; internal set; }
|
||||
public Dictionary<string, object> PosData { get; set; }
|
||||
public List<PaymentEntity> Payments { get; set; }
|
||||
public bool Archived { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -371,6 +371,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
public bool ExtendedNotifications { get; set; }
|
||||
public List<InvoiceEventData> Events { get; internal set; }
|
||||
public double PaymentTolerance { get; set; }
|
||||
public bool Archived { get; set; }
|
||||
|
||||
public bool IsExpired()
|
||||
{
|
||||
|
@ -158,7 +158,8 @@ retry:
|
||||
Status = invoice.StatusString,
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
ItemCode = invoice.ProductInformation.ItemCode,
|
||||
CustomerEmail = invoice.RefundMail
|
||||
CustomerEmail = invoice.RefundMail,
|
||||
Archived = false
|
||||
});
|
||||
|
||||
foreach (var paymentMethod in invoice.GetPaymentMethods())
|
||||
@ -396,6 +397,17 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ToggleInvoiceArchival(string invoiceId, bool archived)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null || invoiceData.Archived == archived )
|
||||
return;
|
||||
invoiceData.Archived = archived;
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
public async Task UpdatePaidInvoiceToInvalid(string invoiceId)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
@ -499,6 +511,7 @@ retry:
|
||||
{
|
||||
entity.BuyerInformation.BuyerEmail = entity.RefundMail;
|
||||
}
|
||||
entity.Archived = invoice.Archived;
|
||||
return entity;
|
||||
}
|
||||
|
||||
@ -513,6 +526,11 @@ retry:
|
||||
{
|
||||
IQueryable<Data.InvoiceData> query = context.Invoices;
|
||||
|
||||
if (!queryObject.IncludeArchived)
|
||||
{
|
||||
query = query.Where(i => !i.Archived);
|
||||
}
|
||||
|
||||
if (queryObject.InvoiceId != null && queryObject.InvoiceId.Length > 0)
|
||||
{
|
||||
var statusSet = queryObject.InvoiceId.ToHashSet().ToArray();
|
||||
@ -838,5 +856,6 @@ retry:
|
||||
public bool IncludeAddresses { get; set; }
|
||||
|
||||
public bool IncludeEvents { get; set; }
|
||||
public bool IncludeArchived { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,21 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<div class="d-flex justify-content-between">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<form asp-action="ToggleArchive" asp-route-invoiceId="@Model.Id" method="post">
|
||||
<button type="submit" class="@(Model.Archived ? "btn badge badge-warning" : "btn btn-link")" id="btn-archive-toggle">
|
||||
@if (Model.Archived)
|
||||
{
|
||||
<span data-toggle="tooltip" title="Unarchive this invoice">Archived <i class=" ml-1 fa fa-close" ></i></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span data-toggle="tooltip" title="Archive this invoice so that it does not appear in the invoice list by default">Archive</span>
|
||||
}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<hr class="primary">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -88,6 +88,7 @@
|
||||
<a class="dropdown-item" href="/invoices?Count=@Model.Count&SearchTerm=exceptionstatus%3ApaidPartial@{@storeIds}">Paid Partial Invoices</a>
|
||||
<a class="dropdown-item" href="/invoices?Count=@Model.Count&SearchTerm=exceptionstatus%3ApaidOver@{@storeIds}">Paid Over Invoices</a>
|
||||
<a class="dropdown-item" href="/invoices?Count=@Model.Count&SearchTerm=unusual%3Atrue@{@storeIds}">Unusual Invoices</a>
|
||||
<a class="dropdown-item" href="/invoices?Count=@Model.Count&SearchTerm=includearchived%3Atrue@{@storeIds}">Archived Invoices</a>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<a class="dropdown-item last24" href="/invoices?Count=@Model.Count&timezoneoffset=0&SearchTerm=startDate%3Alast24@{@storeIds}">Last 24 hours</a>
|
||||
<a class="dropdown-item last72" href="/invoices?Count=@Model.Count&timezoneoffset=0&SearchTerm=startDate%3Alast72@{@storeIds}">Last 3 days</a>
|
||||
@ -220,7 +221,11 @@
|
||||
}
|
||||
</td>
|
||||
<td>@invoice.InvoiceId</td>
|
||||
<td>
|
||||
<td>
|
||||
@if(invoice.Details.Archived)
|
||||
{
|
||||
<span class="badge badge-warning" >archived</span>
|
||||
}
|
||||
@if (invoice.CanMarkStatus)
|
||||
{
|
||||
<div id="pavpill_@invoice.InvoiceId">
|
||||
@ -364,6 +369,7 @@
|
||||
$("a.last24").each(function () { this.href = this.href.replace("last24", getDateStringWithOffset(24)); });
|
||||
$("a.last72").each(function () { this.href = this.href.replace("last72", getDateStringWithOffset(72)); });
|
||||
$("a.last168").each(function () { this.href = this.href.replace("last168", getDateStringWithOffset(168)); });
|
||||
|
||||
});
|
||||
|
||||
function getDateStringWithOffset(hoursDiff) {
|
||||
|
Loading…
Reference in New Issue
Block a user