Adding PaymentType and destination, CSV export

This commit is contained in:
rockstardev 2018-11-30 02:04:26 -06:00
parent 9235d32a45
commit 8e30b7430d
4 changed files with 191 additions and 16 deletions

View file

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

View 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)
{
}
}
}

View file

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

View file

@ -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>