mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Adding PaymentType and destination, CSV export
This commit is contained in:
parent
9235d32a45
commit
8e30b7430d
4 changed files with 191 additions and 16 deletions
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Mime;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -14,6 +15,7 @@ using BTCPayServer.Payments.Changelly;
|
|||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Invoices.Export;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
@ -474,10 +476,18 @@ namespace BTCPayServer.Controllers
|
|||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> Export(string format, string searchTerm = null)
|
||||
{
|
||||
var model = new ExportInvoicesModel();
|
||||
var model = new InvoiceExport();
|
||||
|
||||
var invoices = await ListInvoicesProcess(searchTerm, 0, int.MaxValue);
|
||||
var res = model.Process(invoices, format);
|
||||
|
||||
var cd = new ContentDisposition
|
||||
{
|
||||
FileName = $"btcpay-export-{DateTime.UtcNow.ToString("yyyyMMdd-HHmmss")}.{format}",
|
||||
Inline = true
|
||||
};
|
||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
||||
return Content(res, "application/" + format);
|
||||
}
|
||||
|
||||
|
|
152
BTCPayServer/Services/Invoices/Export/CsvSerializer.cs
Normal file
152
BTCPayServer/Services/Invoices/Export/CsvSerializer.cs
Normal file
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
// Ref: https://www.codeproject.com/Articles/566656/CSV-Serializer-for-NET
|
||||
namespace BTCPayServer.Services.Invoices.Export
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize and Deserialize Lists of any object type to CSV.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1305 // Specify IFormatProvider
|
||||
public class CsvSerializer<T> where T : class, new()
|
||||
{
|
||||
private List<PropertyInfo> _properties;
|
||||
|
||||
public bool IgnoreEmptyLines { get; set; } = true;
|
||||
public bool IgnoreReferenceTypesExceptString { get; set; } = true;
|
||||
public string NewlineReplacement { get; set; } = ((char)0x254).ToString();
|
||||
|
||||
public char Separator { get; set; } = ',';
|
||||
public string Replacement { get; set; } = ((char)0x255).ToString();
|
||||
|
||||
public string RowNumberColumnTitle { get; set; } = "RowNumber";
|
||||
public bool UseLineNumbers { get; set; } = false;
|
||||
public bool UseTextQualifier { get; set; } = false;
|
||||
public bool UseEofLiteral { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Csv Serializer
|
||||
/// Initialize by selected properties from the type to be de/serialized
|
||||
/// </summary>
|
||||
public CsvSerializer()
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance
|
||||
| BindingFlags.GetProperty | BindingFlags.SetProperty);
|
||||
|
||||
|
||||
var q = properties.AsQueryable();
|
||||
|
||||
if (IgnoreReferenceTypesExceptString)
|
||||
{
|
||||
q = q.Where(a => a.PropertyType.IsValueType || a.PropertyType.Name == "String");
|
||||
}
|
||||
|
||||
var r = from a in q
|
||||
where a.GetCustomAttribute<CsvIgnoreAttribute>() == null
|
||||
select a;
|
||||
|
||||
_properties = r.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize
|
||||
/// </summary>
|
||||
/// <param name="stream">stream</param>
|
||||
/// <param name="data">data</param>
|
||||
public string Serialize(IList<T> data)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var values = new List<string>();
|
||||
|
||||
sb.AppendLine(GetHeader());
|
||||
|
||||
var row = 1;
|
||||
foreach (var item in data)
|
||||
{
|
||||
values.Clear();
|
||||
|
||||
if (UseLineNumbers)
|
||||
{
|
||||
values.Add(row++.ToString());
|
||||
}
|
||||
|
||||
foreach (var p in _properties)
|
||||
{
|
||||
var raw = p.GetValue(item);
|
||||
var value = raw == null ? "" :
|
||||
raw.ToString()
|
||||
.Replace(Separator.ToString(), Replacement)
|
||||
.Replace(Environment.NewLine, NewlineReplacement);
|
||||
|
||||
if (UseTextQualifier)
|
||||
{
|
||||
value = string.Format("\"{0}\"", value);
|
||||
}
|
||||
|
||||
values.Add(value);
|
||||
}
|
||||
sb.AppendLine(string.Join(Separator.ToString(), values.ToArray()));
|
||||
}
|
||||
|
||||
if (UseEofLiteral)
|
||||
{
|
||||
values.Clear();
|
||||
|
||||
if (UseLineNumbers)
|
||||
{
|
||||
values.Add(row++.ToString());
|
||||
}
|
||||
|
||||
values.Add("EOF");
|
||||
|
||||
sb.AppendLine(string.Join(Separator.ToString(), values.ToArray()));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Header
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string GetHeader()
|
||||
{
|
||||
var header = _properties.Select(a => a.Name);
|
||||
|
||||
if (UseLineNumbers)
|
||||
{
|
||||
header = new string[] { RowNumberColumnTitle }.Union(header);
|
||||
}
|
||||
|
||||
return string.Join(Separator.ToString(), header.ToArray());
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1305 // Specify IFormatProvider
|
||||
|
||||
public class CsvIgnoreAttribute : Attribute { }
|
||||
|
||||
public class InvalidCsvFormatException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Invalid Csv Format Exception
|
||||
/// </summary>
|
||||
/// <param name="message">message</param>
|
||||
public InvalidCsvFormatException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public InvalidCsvFormatException(string message, Exception ex)
|
||||
: base(message, ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,19 +6,11 @@ using BTCPayServer.Payments.Bitcoin;
|
|||
using BTCPayServer.Services.Invoices;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Models.InvoicingModels
|
||||
namespace BTCPayServer.Services.Invoices.Export
|
||||
{
|
||||
public class ExportInvoicesModel
|
||||
public class InvoiceExport
|
||||
{
|
||||
public string Process(InvoiceEntity[] invoices, string fileFormat)
|
||||
{
|
||||
if (String.Equals(fileFormat, "json", StringComparison.OrdinalIgnoreCase))
|
||||
return processJson(invoices);
|
||||
else
|
||||
throw new Exception("Export format not supported");
|
||||
}
|
||||
|
||||
private string processJson(InvoiceEntity[] invoices)
|
||||
{
|
||||
var csvInvoices = new List<ExportInvoiceHolder>();
|
||||
foreach (var i in invoices)
|
||||
|
@ -26,12 +18,30 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||
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(csvInvoices, Formatting.Indented, serializerSett);
|
||||
var json = JsonConvert.SerializeObject(invoices, Formatting.Indented, serializerSett);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
private string processCsv(List<ExportInvoiceHolder> invoices)
|
||||
{
|
||||
var serializer = new CsvSerializer<ExportInvoiceHolder>();
|
||||
var csv = serializer.Serialize(invoices);
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
private IEnumerable<ExportInvoiceHolder> convertFromDb(InvoiceEntity invoice)
|
||||
{
|
||||
var exportList = new List<ExportInvoiceHolder>();
|
||||
|
@ -43,14 +53,15 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||
|
||||
var pmethod = invoice.GetPaymentMethod(payment.GetPaymentMethodId(), null);
|
||||
var accounting = pmethod.Calculate();
|
||||
var onchainDetails = pmethod.GetPaymentMethodDetails() as BitcoinLikeOnChainPaymentMethod;
|
||||
var details = pmethod.GetPaymentMethodDetails();
|
||||
|
||||
var target = new ExportInvoiceHolder
|
||||
{
|
||||
PaymentId = pdata.GetPaymentId(),
|
||||
CryptoCode = cryptoCode,
|
||||
ConversionRate = pmethod.Rate,
|
||||
Address = onchainDetails?.DepositAddress,
|
||||
PaymentType = details.GetPaymentType().ToString(),
|
||||
Destination = details.GetPaymentDestination(),
|
||||
PaymentDue = $"{accounting.MinimumTotalDue} {cryptoCode}",
|
||||
PaymentPaid = $"{accounting.CryptoPaid} {cryptoCode}",
|
||||
PaymentOverpaid = $"{accounting.OverpaidHelper} {cryptoCode}",
|
||||
|
@ -78,7 +89,8 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||
public string PaymentId { get; set; }
|
||||
public string CryptoCode { get; set; }
|
||||
public decimal ConversionRate { get; set; }
|
||||
public string Address { get; set; }
|
||||
public string PaymentType { get; set; }
|
||||
public string Destination { get; set; }
|
||||
public string PaymentDue { get; set; }
|
||||
public string PaymentPaid { get; set; }
|
||||
public string PaymentOverpaid { get; set; }
|
|
@ -48,7 +48,8 @@
|
|||
Export
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<a asp-action="Export" asp-route-format="json" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item" target="_blank">CSV</a>
|
||||
<a asp-action="Export" asp-route-format="csv" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item" target="_blank">CSV</a>
|
||||
<a asp-action="Export" asp-route-format="json" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item" target="_blank">JSON</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue