mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-10 09:19:24 +01:00
Remove legacy confusing export (#5293)
This commit is contained in:
parent
445e1b7bd9
commit
2d38113c66
4 changed files with 9 additions and 355 deletions
|
@ -1700,109 +1700,6 @@ namespace BTCPayServer.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = LongRunningTestTimeout)]
|
|
||||||
[Trait("Integration", "Integration")]
|
|
||||||
public async Task CanExportInvoicesJson()
|
|
||||||
{
|
|
||||||
decimal GetFieldValue(string input, string fieldName)
|
|
||||||
{
|
|
||||||
var match = Regex.Match(input, $"\"{fieldName}\":([^,]*)");
|
|
||||||
Assert.True(match.Success);
|
|
||||||
return decimal.Parse(match.Groups[1].Value.Trim(), CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<object[]> GetExport(TestAccount account, string storeId = null)
|
|
||||||
{
|
|
||||||
var content = await account.GetController<UIInvoiceController>(false)
|
|
||||||
.Export("json", storeId);
|
|
||||||
var result = Assert.IsType<ContentResult>(content);
|
|
||||||
Assert.Equal("application/json", result.ContentType);
|
|
||||||
return JsonConvert.DeserializeObject<object[]>(result.Content ?? "[]");
|
|
||||||
}
|
|
||||||
|
|
||||||
using var tester = CreateServerTester();
|
|
||||||
await tester.StartAsync();
|
|
||||||
var user = tester.NewAccount();
|
|
||||||
await user.GrantAccessAsync();
|
|
||||||
user.RegisterDerivationScheme("BTC");
|
|
||||||
await user.SetNetworkFeeMode(NetworkFeeMode.Always);
|
|
||||||
var invoice = await user.BitPay.CreateInvoiceAsync(
|
|
||||||
new Invoice
|
|
||||||
{
|
|
||||||
Price = 10,
|
|
||||||
Currency = "USD",
|
|
||||||
PosData = "posData",
|
|
||||||
OrderId = "orderId",
|
|
||||||
ItemDesc = "Some \", description",
|
|
||||||
FullNotifications = true
|
|
||||||
}, Facade.Merchant);
|
|
||||||
|
|
||||||
var networkFee = new FeeRate(invoice.MinerFees["BTC"].SatoshiPerBytes).GetFee(100);
|
|
||||||
var result = await GetExport(user);
|
|
||||||
Assert.Single(result);
|
|
||||||
|
|
||||||
var cashCow = tester.ExplorerNode;
|
|
||||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
|
|
||||||
var firstPayment = invoice.CryptoInfo[0].TotalDue - 3 * networkFee;
|
|
||||||
cashCow.SendToAddress(invoiceAddress, firstPayment);
|
|
||||||
Thread.Sleep(1000); // prevent race conditions, ordering payments
|
|
||||||
// look if you can reduce thread sleep, this was min value for me
|
|
||||||
|
|
||||||
// should reduce invoice due by 0 USD because payment = network fee
|
|
||||||
cashCow.SendToAddress(invoiceAddress, networkFee);
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
|
|
||||||
// pay remaining amount
|
|
||||||
cashCow.SendToAddress(invoiceAddress, 4 * networkFee);
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
|
|
||||||
await TestUtils.EventuallyAsync(async () =>
|
|
||||||
{
|
|
||||||
var parsedJson = await GetExport(user);
|
|
||||||
Assert.Equal(3, parsedJson.Length);
|
|
||||||
|
|
||||||
var invoiceDueAfterFirstPayment = 3 * networkFee.ToDecimal(MoneyUnit.BTC) * invoice.Rate;
|
|
||||||
var pay1str = parsedJson[0].ToString();
|
|
||||||
Assert.Contains("\"InvoiceItemDesc\": \"Some \\\", description\"", pay1str);
|
|
||||||
Assert.Equal(invoiceDueAfterFirstPayment, GetFieldValue(pay1str, "InvoiceDue"));
|
|
||||||
Assert.Contains("\"InvoicePrice\": 10.0", pay1str);
|
|
||||||
Assert.Contains("\"ConversionRate\": 5000.0", pay1str);
|
|
||||||
Assert.Contains($"\"InvoiceId\": \"{invoice.Id}\",", pay1str);
|
|
||||||
|
|
||||||
var pay2str = parsedJson[1].ToString();
|
|
||||||
Assert.Equal(invoiceDueAfterFirstPayment, GetFieldValue(pay2str, "InvoiceDue"));
|
|
||||||
|
|
||||||
var pay3str = parsedJson[2].ToString();
|
|
||||||
Assert.Contains("\"InvoiceDue\": 0", pay3str);
|
|
||||||
});
|
|
||||||
|
|
||||||
// create an invoice for a new store and check responses with and without store id
|
|
||||||
var otherUser = tester.NewAccount();
|
|
||||||
await otherUser.GrantAccessAsync();
|
|
||||||
otherUser.RegisterDerivationScheme("BTC");
|
|
||||||
await otherUser.SetNetworkFeeMode(NetworkFeeMode.Always);
|
|
||||||
var newInvoice = await otherUser.BitPay.CreateInvoiceAsync(
|
|
||||||
new Invoice
|
|
||||||
{
|
|
||||||
Price = 21,
|
|
||||||
Currency = "USD",
|
|
||||||
PosData = "posData",
|
|
||||||
OrderId = "orderId",
|
|
||||||
ItemDesc = "Some \", description",
|
|
||||||
FullNotifications = true
|
|
||||||
}, Facade.Merchant);
|
|
||||||
|
|
||||||
await otherUser.PayInvoice(newInvoice.Id);
|
|
||||||
Assert.Single(await GetExport(otherUser));
|
|
||||||
Assert.Single(await GetExport(otherUser, otherUser.StoreId));
|
|
||||||
Assert.Equal(3, (await GetExport(user, user.StoreId)).Length);
|
|
||||||
Assert.Equal(3, (await GetExport(user)).Length);
|
|
||||||
|
|
||||||
await otherUser.AddOwner(user.UserId);
|
|
||||||
Assert.Equal(4, (await GetExport(user)).Length);
|
|
||||||
Assert.Single(await GetExport(user, otherUser.StoreId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact(Timeout = LongRunningTestTimeout)]
|
[Fact(Timeout = LongRunningTestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task CanChangeNetworkFeeMode()
|
public async Task CanChangeNetworkFeeMode()
|
||||||
|
@ -1892,45 +1789,6 @@ namespace BTCPayServer.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = LongRunningTestTimeout)]
|
|
||||||
[Trait("Integration", "Integration")]
|
|
||||||
public async Task CanExportInvoicesCsv()
|
|
||||||
{
|
|
||||||
using var tester = CreateServerTester();
|
|
||||||
await tester.StartAsync();
|
|
||||||
var user = tester.NewAccount();
|
|
||||||
await user.GrantAccessAsync();
|
|
||||||
user.RegisterDerivationScheme("BTC");
|
|
||||||
await user.SetNetworkFeeMode(NetworkFeeMode.Always);
|
|
||||||
var invoice = user.BitPay.CreateInvoice(
|
|
||||||
new Invoice
|
|
||||||
{
|
|
||||||
Price = 500,
|
|
||||||
Currency = "USD",
|
|
||||||
PosData = "posData",
|
|
||||||
OrderId = "orderId",
|
|
||||||
ItemDesc = "Some \", description",
|
|
||||||
FullNotifications = true
|
|
||||||
}, Facade.Merchant);
|
|
||||||
|
|
||||||
var cashCow = tester.ExplorerNode;
|
|
||||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
|
|
||||||
var firstPayment = invoice.CryptoInfo[0].TotalDue - Money.Coins(0.001m);
|
|
||||||
cashCow.SendToAddress(invoiceAddress, firstPayment);
|
|
||||||
TestUtils.Eventually(() =>
|
|
||||||
{
|
|
||||||
var exportResultPaid =
|
|
||||||
user.GetController<UIInvoiceController>().Export("csv").GetAwaiter().GetResult();
|
|
||||||
var paidresult = Assert.IsType<ContentResult>(exportResultPaid);
|
|
||||||
Assert.Equal("application/csv", paidresult.ContentType);
|
|
||||||
Assert.Contains($",orderId,{invoice.Id},", paidresult.Content);
|
|
||||||
Assert.Contains($",On-Chain,BTC,0.0991,0.0001,5000.0", paidresult.Content);
|
|
||||||
Assert.Contains($",USD,5.00", paidresult.Content); // Seems hacky but some plateform does not render this decimal the same
|
|
||||||
Assert.Contains("0,,\"Some \"\", description\",New (paidPartial),new,paidPartial",
|
|
||||||
paidresult.Content);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact(Timeout = LongRunningTestTimeout)]
|
[Fact(Timeout = LongRunningTestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task CanCreateAndDeleteApps()
|
public async Task CanCreateAndDeleteApps()
|
||||||
|
|
|
@ -24,7 +24,6 @@ using BTCPayServer.Rating;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Invoices.Export;
|
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -1182,42 +1181,6 @@ namespace BTCPayServer.Controllers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewInvoices)]
|
|
||||||
[BitpayAPIConstraint(false)]
|
|
||||||
public async Task<IActionResult> Export(string format, string? storeId = null, string? searchTerm = null, int timezoneOffset = 0)
|
|
||||||
{
|
|
||||||
var model = new InvoiceExport(_CurrencyNameTable);
|
|
||||||
var fs = new SearchString(searchTerm);
|
|
||||||
var storeIds = new HashSet<string>();
|
|
||||||
if (storeId is not null)
|
|
||||||
{
|
|
||||||
storeIds.Add(storeId);
|
|
||||||
}
|
|
||||||
if (fs.GetFilterArray("storeid") is { } l)
|
|
||||||
{
|
|
||||||
foreach (var i in l)
|
|
||||||
storeIds.Add(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
var apps = await _appService.GetAllApps(GetUserId(), false, storeId);
|
|
||||||
InvoiceQuery invoiceQuery = GetInvoiceQuery(fs, apps, timezoneOffset);
|
|
||||||
invoiceQuery.StoreId = storeIds.ToArray();
|
|
||||||
invoiceQuery.Skip = 0;
|
|
||||||
invoiceQuery.Take = int.MaxValue;
|
|
||||||
var invoices = await _InvoiceRepository.GetInvoices(invoiceQuery);
|
|
||||||
var res = model.Process(invoices, format);
|
|
||||||
|
|
||||||
var cd = new ContentDisposition
|
|
||||||
{
|
|
||||||
FileName = $"btcpay-export-{DateTime.UtcNow.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture)}.{format}",
|
|
||||||
Inline = true
|
|
||||||
};
|
|
||||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
|
||||||
Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
|
||||||
return Content(res, "application/" + format);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SelectList GetPaymentMethodsSelectList()
|
private SelectList GetPaymentMethodsSelectList()
|
||||||
{
|
{
|
||||||
var store = GetCurrentStore();
|
var store = GetCurrentStore();
|
||||||
|
|
|
@ -1,169 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using BTCPayServer.Services.Rates;
|
|
||||||
using CsvHelper.Configuration;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Invoices.Export
|
|
||||||
{
|
|
||||||
public class InvoiceExport
|
|
||||||
{
|
|
||||||
public BTCPayNetworkProvider Networks { get; }
|
|
||||||
public CurrencyNameTable Currencies { get; }
|
|
||||||
|
|
||||||
public InvoiceExport(CurrencyNameTable currencies)
|
|
||||||
{
|
|
||||||
Currencies = currencies;
|
|
||||||
}
|
|
||||||
public string Process(InvoiceEntity[] invoices, string fileFormat)
|
|
||||||
{
|
|
||||||
var csvInvoices = new List<ExportInvoiceHolder>();
|
|
||||||
foreach (var i in invoices)
|
|
||||||
{
|
|
||||||
csvInvoices.AddRange(convertFromDb(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (String.Equals(fileFormat, "json", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return processJson(csvInvoices);
|
|
||||||
else if (String.Equals(fileFormat, "csv", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return processCsv(csvInvoices);
|
|
||||||
else
|
|
||||||
throw new Exception("Export format not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
private string processJson(List<ExportInvoiceHolder> invoices)
|
|
||||||
{
|
|
||||||
var serializerSett = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
|
|
||||||
var json = JsonConvert.SerializeObject(invoices, Formatting.Indented, serializerSett);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string processCsv(List<ExportInvoiceHolder> invoices)
|
|
||||||
{
|
|
||||||
using StringWriter writer = new StringWriter();
|
|
||||||
using var csvWriter = new CsvHelper.CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture), true);
|
|
||||||
csvWriter.WriteHeader<ExportInvoiceHolder>();
|
|
||||||
csvWriter.NextRecord();
|
|
||||||
csvWriter.WriteRecords(invoices);
|
|
||||||
csvWriter.Flush();
|
|
||||||
return writer.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<ExportInvoiceHolder> convertFromDb(InvoiceEntity invoice)
|
|
||||||
{
|
|
||||||
var exportList = new List<ExportInvoiceHolder>();
|
|
||||||
var currency = Currencies.GetNumberFormatInfo(invoice.Currency, true);
|
|
||||||
var invoiceDue = invoice.Price;
|
|
||||||
var payments = invoice.GetPayments(false);
|
|
||||||
// Get invoices with payments
|
|
||||||
if (payments.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var payment in payments)
|
|
||||||
{
|
|
||||||
var pdata = payment.GetCryptoPaymentData();
|
|
||||||
invoiceDue -= payment.InvoicePaidAmount.Net;
|
|
||||||
|
|
||||||
var target = new ExportInvoiceHolder
|
|
||||||
{
|
|
||||||
ReceivedDate = payment.ReceivedTime.UtcDateTime,
|
|
||||||
PaymentId = pdata.GetPaymentId(),
|
|
||||||
CryptoCode = payment.Currency,
|
|
||||||
ConversionRate = payment.Rate,
|
|
||||||
PaymentType = payment.GetPaymentMethodId().PaymentType.ToPrettyString(),
|
|
||||||
Destination = pdata.GetDestination(),
|
|
||||||
Paid = payment.PaidAmount.Gross.ToString(CultureInfo.InvariantCulture),
|
|
||||||
PaidCurrency = Math.Round(payment.InvoicePaidAmount.Gross, currency.NumberDecimalDigits).ToString(CultureInfo.InvariantCulture),
|
|
||||||
// Adding NetworkFee because Paid doesn't take into account network fees
|
|
||||||
// so if fee is 10000 satoshis, customer can essentially send infinite number of tx
|
|
||||||
// and merchant effectivelly would receive 0 BTC, invoice won't be paid
|
|
||||||
// while looking just at export you could sum Paid and assume merchant "received payments"
|
|
||||||
NetworkFee = payment.NetworkFee.ToString(CultureInfo.InvariantCulture),
|
|
||||||
InvoiceDue = Math.Round(invoiceDue, currency.NumberDecimalDigits),
|
|
||||||
OrderId = invoice.Metadata.OrderId ?? string.Empty,
|
|
||||||
StoreId = invoice.StoreId,
|
|
||||||
InvoiceId = invoice.Id,
|
|
||||||
InvoiceCreatedDate = invoice.InvoiceTime.UtcDateTime,
|
|
||||||
InvoiceExpirationDate = invoice.ExpirationTime.UtcDateTime,
|
|
||||||
InvoiceMonitoringDate = invoice.MonitoringExpiration.UtcDateTime,
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
InvoiceFullStatus = invoice.GetInvoiceState().ToString(),
|
|
||||||
InvoiceStatus = invoice.StatusString,
|
|
||||||
InvoiceExceptionStatus = invoice.ExceptionStatusString,
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
InvoiceItemCode = invoice.Metadata.ItemCode,
|
|
||||||
InvoiceItemDesc = invoice.Metadata.ItemDesc,
|
|
||||||
InvoicePrice = invoice.Price,
|
|
||||||
InvoiceCurrency = invoice.Currency,
|
|
||||||
BuyerEmail = invoice.Metadata.BuyerEmail,
|
|
||||||
Accounted = payment.Accounted
|
|
||||||
};
|
|
||||||
|
|
||||||
exportList.Add(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var target = new ExportInvoiceHolder
|
|
||||||
{
|
|
||||||
InvoiceDue = Math.Round(invoiceDue, currency.NumberDecimalDigits),
|
|
||||||
OrderId = invoice.Metadata.OrderId ?? string.Empty,
|
|
||||||
StoreId = invoice.StoreId,
|
|
||||||
InvoiceId = invoice.Id,
|
|
||||||
InvoiceCreatedDate = invoice.InvoiceTime.UtcDateTime,
|
|
||||||
InvoiceExpirationDate = invoice.ExpirationTime.UtcDateTime,
|
|
||||||
InvoiceMonitoringDate = invoice.MonitoringExpiration.UtcDateTime,
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
InvoiceFullStatus = invoice.GetInvoiceState().ToString(),
|
|
||||||
InvoiceStatus = invoice.StatusString,
|
|
||||||
InvoiceExceptionStatus = invoice.ExceptionStatusString,
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
InvoiceItemCode = invoice.Metadata.ItemCode,
|
|
||||||
InvoiceItemDesc = invoice.Metadata.ItemDesc,
|
|
||||||
InvoicePrice = invoice.Price,
|
|
||||||
InvoiceCurrency = invoice.Currency,
|
|
||||||
BuyerEmail = invoice.Metadata.BuyerEmail
|
|
||||||
};
|
|
||||||
|
|
||||||
exportList.Add(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
exportList = exportList.OrderBy(a => a.ReceivedDate).ToList();
|
|
||||||
|
|
||||||
return exportList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ExportInvoiceHolder
|
|
||||||
{
|
|
||||||
public DateTime? ReceivedDate { get; set; }
|
|
||||||
public string StoreId { get; set; }
|
|
||||||
public string OrderId { get; set; }
|
|
||||||
public string InvoiceId { get; set; }
|
|
||||||
public DateTime InvoiceCreatedDate { get; set; }
|
|
||||||
public DateTime InvoiceExpirationDate { get; set; }
|
|
||||||
public DateTime InvoiceMonitoringDate { get; set; }
|
|
||||||
|
|
||||||
public string PaymentId { get; set; }
|
|
||||||
public string Destination { get; set; }
|
|
||||||
public string PaymentType { get; set; }
|
|
||||||
public string CryptoCode { get; set; }
|
|
||||||
public string Paid { get; set; }
|
|
||||||
public string NetworkFee { get; set; }
|
|
||||||
public decimal ConversionRate { get; set; }
|
|
||||||
public string PaidCurrency { get; set; }
|
|
||||||
public string InvoiceCurrency { get; set; }
|
|
||||||
public decimal InvoiceDue { get; set; }
|
|
||||||
public decimal InvoicePrice { get; set; }
|
|
||||||
public string InvoiceItemCode { get; set; }
|
|
||||||
public string InvoiceItemDesc { get; set; }
|
|
||||||
public string InvoiceFullStatus { get; set; }
|
|
||||||
public string InvoiceStatus { get; set; }
|
|
||||||
public string InvoiceExceptionStatus { get; set; }
|
|
||||||
public string BuyerEmail { get; set; }
|
|
||||||
public bool Accounted { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,13 @@
|
||||||
@using BTCPayServer.Client
|
@using BTCPayServer.Client
|
||||||
@using BTCPayServer.Client.Models
|
@using BTCPayServer.Client.Models
|
||||||
@using BTCPayServer.Services
|
@using BTCPayServer.Services
|
||||||
|
|
||||||
@inject DisplayFormatter DisplayFormatter
|
@inject DisplayFormatter DisplayFormatter
|
||||||
|
@inject ReportService ReportService
|
||||||
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
|
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
|
||||||
@model InvoicesModel
|
@model InvoicesModel
|
||||||
@{
|
@{
|
||||||
|
var reportNames = ReportService.ReportProviders.Select(p => p.Value.Name).OrderBy(c => c).ToArray();
|
||||||
ViewData.SetActivePage(InvoiceNavPages.Index, "Invoices");
|
ViewData.SetActivePage(InvoiceNavPages.Index, "Invoices");
|
||||||
var statusFilterCount = CountArrayFilter("status") + CountArrayFilter("exceptionstatus") + (HasBooleanFilter("includearchived") ? 1 : 0) + (HasBooleanFilter("unusual") ? 1 : 0);
|
var statusFilterCount = CountArrayFilter("status") + CountArrayFilter("exceptionstatus") + (HasBooleanFilter("includearchived") ? 1 : 0) + (HasBooleanFilter("unusual") ? 1 : 0);
|
||||||
var hasDateFilter = HasArrayFilter("startdate") || HasArrayFilter("enddate");
|
var hasDateFilter = HasArrayFilter("startdate") || HasArrayFilter("enddate");
|
||||||
|
@ -116,9 +119,6 @@
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
var timezoneOffset = new Date().getTimezoneOffset();
|
var timezoneOffset = new Date().getTimezoneOffset();
|
||||||
$(".export-link, a.dropdown-item").each(function () {
|
|
||||||
this.href = this.href.replace("timezoneoffset=0", "timezoneoffset=" + timezoneOffset);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#invoices")
|
$("#invoices")
|
||||||
.on("click", ".invoice-row .invoice-details-toggle", function (e) {
|
.on("click", ".invoice-row .invoice-details-toggle", function (e) {
|
||||||
|
@ -335,16 +335,18 @@
|
||||||
{
|
{
|
||||||
<button type="submit" asp-action="MassAction" class="dropdown-item" name="command" value="unarchive" id="ActionsDropdownUnarchive">Unarchive</button>
|
<button type="submit" asp-action="MassAction" class="dropdown-item" name="command" value="unarchive" id="ActionsDropdownUnarchive">Unarchive</button>
|
||||||
}
|
}
|
||||||
<button id="BumpFee" type="submit" permission="@Policies.CanModifyStoreSettings" class="dropdown-item" name="command" value="cpfp">Bump fee</button>
|
<button id="BumpFee" type="submit" permission="@Policies.CanModifyStoreSettings" class="dropdown-item" name="command" value="cpfp">Bump fee</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown d-inline-flex align-items-center gap-3">
|
<div class="dropdown d-inline-flex align-items-center gap-3">
|
||||||
<button class="btn btn-secondary dropdown-toggle dropdown-toggle-custom-caret order-xxl-1" type="button" id="ExportDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button class="btn btn-secondary dropdown-toggle dropdown-toggle-custom-caret order-xxl-1" type="button" id="ExportDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
Export
|
Reports
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu" aria-labelledby="ExportDropdownToggle">
|
<div class="dropdown-menu" aria-labelledby="ExportDropdownToggle">
|
||||||
<a asp-action="Export" asp-route-timezoneoffset="0" asp-route-format="csv" asp-route-storeId="@Model.StoreId" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item export-link" target="_blank">CSV</a>
|
@foreach (var report in reportNames)
|
||||||
<a asp-action="Export" asp-route-timezoneoffset="0" asp-route-format="json" asp-route-storeId="@Model.StoreId" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item export-link" target="_blank">JSON</a>
|
{
|
||||||
|
<a asp-controller="UIReports" asp-action="StoreReports" asp-route-viewName="@report" asp-route-storeId="@Model.StoreId" class="dropdown-item export-link">@report</a>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<a href="https://docs.btcpayserver.org/Accounting/" target="_blank" rel="noreferrer noopener" title="More information...">
|
<a href="https://docs.btcpayserver.org/Accounting/" target="_blank" rel="noreferrer noopener" title="More information...">
|
||||||
<vc:icon symbol="info" />
|
<vc:icon symbol="info" />
|
||||||
|
|
Loading…
Add table
Reference in a new issue