Archive Invoice

This commit is contained in:
Kukks
2020-05-07 12:50:07 +02:00
parent 137c3ef2ce
commit e5a3ef3e22
9 changed files with 107 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -79,6 +79,8 @@ namespace BTCPayServer.Data
{ {
get; set; get; set;
} }
public bool Archived { get; set; }
public List<PendingInvoiceData> PendingInvoices { get; set; } public List<PendingInvoiceData> PendingInvoices { get; set; }
} }
} }

View File

@@ -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");
}
}
}
}

View File

@@ -190,6 +190,9 @@ namespace BTCPayServer.Migrations
b.Property<string>("Id") b.Property<string>("Id")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool>("Archived")
.HasColumnType("INTEGER");
b.Property<byte[]>("Blob") b.Property<byte[]>("Blob")
.HasColumnType("BLOB"); .HasColumnType("BLOB");

View File

@@ -41,7 +41,8 @@ namespace BTCPayServer.Controllers
InvoiceId = new[] {invoiceId}, InvoiceId = new[] {invoiceId},
UserId = GetUserId(), UserId = GetUserId(),
IncludeAddresses = true, IncludeAddresses = true,
IncludeEvents = true IncludeEvents = true,
IncludeArchived = true,
})).FirstOrDefault(); })).FirstOrDefault();
if (invoice == null) if (invoice == null)
return NotFound(); return NotFound();
@@ -71,7 +72,8 @@ namespace BTCPayServer.Controllers
ProductInformation = invoice.ProductInformation, ProductInformation = invoice.ProductInformation,
StatusException = invoice.ExceptionStatus, StatusException = invoice.ExceptionStatus,
Events = invoice.Events, Events = invoice.Events,
PosData = PosDataParser.ParsePosData(invoice.PosData) PosData = PosDataParser.ParsePosData(invoice.PosData),
Archived = invoice.Archived
}; };
model.Addresses = invoice.HistoricalAddresses.Select(h => model.Addresses = invoice.HistoricalAddresses.Select(h =>
@@ -91,6 +93,7 @@ namespace BTCPayServer.Controllers
private InvoiceDetailsModel InvoicePopulatePayments(InvoiceEntity invoice) private InvoiceDetailsModel InvoicePopulatePayments(InvoiceEntity invoice)
{ {
var model = new InvoiceDetailsModel(); var model = new InvoiceDetailsModel();
model.Archived = invoice.Archived;
model.Payments = invoice.GetPayments(); model.Payments = invoice.GetPayments();
foreach (var data in invoice.GetPaymentMethods()) foreach (var data in invoice.GetPaymentMethods())
{ {
@@ -111,6 +114,26 @@ namespace BTCPayServer.Controllers
return model; 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] [HttpGet]
[Route("i/{invoiceId}")] [Route("i/{invoiceId}")]
[Route("i/{invoiceId}/{paymentMethodId}")] [Route("i/{invoiceId}/{paymentMethodId}")]
@@ -437,7 +460,7 @@ namespace BTCPayServer.Controllers
AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.ProductInformation.Price, invoice.ProductInformation.Currency), AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.ProductInformation.Price, invoice.ProductInformation.Currency),
CanMarkInvalid = state.CanMarkInvalid(), CanMarkInvalid = state.CanMarkInvalid(),
CanMarkComplete = state.CanMarkComplete(), CanMarkComplete = state.CanMarkComplete(),
Details = InvoicePopulatePayments(invoice) Details = InvoicePopulatePayments(invoice),
}); });
} }
model.Total = await counting; model.Total = await counting;
@@ -452,6 +475,7 @@ namespace BTCPayServer.Controllers
TextSearch = fs.TextSearch, TextSearch = fs.TextSearch,
UserId = GetUserId(), UserId = GetUserId(),
Unusual = fs.GetFilterBool("unusual"), Unusual = fs.GetFilterBool("unusual"),
IncludeArchived = fs.GetFilterBool("includearchived") ?? false,
Status = fs.GetFilterArray("status"), Status = fs.GetFilterArray("status"),
ExceptionStatus = fs.GetFilterArray("exceptionstatus"), ExceptionStatus = fs.GetFilterArray("exceptionstatus"),
StoreId = fs.GetFilterArray("storeid"), StoreId = fs.GetFilterArray("storeid"),

View File

@@ -127,5 +127,6 @@ namespace BTCPayServer.Models.InvoicingModels
public string NotificationEmail { get; internal set; } public string NotificationEmail { get; internal set; }
public Dictionary<string, object> PosData { get; set; } public Dictionary<string, object> PosData { get; set; }
public List<PaymentEntity> Payments { get; set; } public List<PaymentEntity> Payments { get; set; }
public bool Archived { get; set; }
} }
} }

View File

@@ -371,6 +371,7 @@ namespace BTCPayServer.Services.Invoices
public bool ExtendedNotifications { get; set; } public bool ExtendedNotifications { get; set; }
public List<InvoiceEventData> Events { get; internal set; } public List<InvoiceEventData> Events { get; internal set; }
public double PaymentTolerance { get; set; } public double PaymentTolerance { get; set; }
public bool Archived { get; set; }
public bool IsExpired() public bool IsExpired()
{ {

View File

@@ -158,7 +158,8 @@ retry:
Status = invoice.StatusString, Status = invoice.StatusString,
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete
ItemCode = invoice.ProductInformation.ItemCode, ItemCode = invoice.ProductInformation.ItemCode,
CustomerEmail = invoice.RefundMail CustomerEmail = invoice.RefundMail,
Archived = false
}); });
foreach (var paymentMethod in invoice.GetPaymentMethods()) 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) public async Task UpdatePaidInvoiceToInvalid(string invoiceId)
{ {
using (var context = _ContextFactory.CreateContext()) using (var context = _ContextFactory.CreateContext())
@@ -499,6 +511,7 @@ retry:
{ {
entity.BuyerInformation.BuyerEmail = entity.RefundMail; entity.BuyerInformation.BuyerEmail = entity.RefundMail;
} }
entity.Archived = invoice.Archived;
return entity; return entity;
} }
@@ -513,6 +526,11 @@ retry:
{ {
IQueryable<Data.InvoiceData> query = context.Invoices; IQueryable<Data.InvoiceData> query = context.Invoices;
if (!queryObject.IncludeArchived)
{
query = query.Where(i => !i.Archived);
}
if (queryObject.InvoiceId != null && queryObject.InvoiceId.Length > 0) if (queryObject.InvoiceId != null && queryObject.InvoiceId.Length > 0)
{ {
var statusSet = queryObject.InvoiceId.ToHashSet().ToArray(); var statusSet = queryObject.InvoiceId.ToHashSet().ToArray();
@@ -838,5 +856,6 @@ retry:
public bool IncludeAddresses { get; set; } public bool IncludeAddresses { get; set; }
public bool IncludeEvents { get; set; } public bool IncludeEvents { get; set; }
public bool IncludeArchived { get; set; } = true;
} }
} }

View File

@@ -28,7 +28,21 @@
<div class="row"> <div class="row">
<div class="col-lg-12 section-heading"> <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")">
@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"> <hr class="primary">
</div> </div>
</div> </div>

View File

@@ -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%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=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=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> <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 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> <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>
<td>@invoice.InvoiceId</td> <td>@invoice.InvoiceId</td>
<td> <td>
@if(invoice.Details.Archived)
{
<span class="badge badge-warning" >archived</span>
}
@if (invoice.CanMarkStatus) @if (invoice.CanMarkStatus)
{ {
<div id="pavpill_@invoice.InvoiceId"> <div id="pavpill_@invoice.InvoiceId">
@@ -364,6 +369,7 @@
$("a.last24").each(function () { this.href = this.href.replace("last24", getDateStringWithOffset(24)); }); $("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.last72").each(function () { this.href = this.href.replace("last72", getDateStringWithOffset(72)); });
$("a.last168").each(function () { this.href = this.href.replace("last168", getDateStringWithOffset(168)); }); $("a.last168").each(function () { this.href = this.href.replace("last168", getDateStringWithOffset(168)); });
}); });
function getDateStringWithOffset(hoursDiff) { function getDateStringWithOffset(hoursDiff) {