From 9235d32a45eac544190613e8be19b13d4bf09af8 Mon Sep 17 00:00:00 2001 From: rockstardev Date: Fri, 30 Nov 2018 01:22:39 -0600 Subject: [PATCH] Export of payments made on invoices --- .../Controllers/InvoiceController.UI.cs | 22 ++++- .../InvoicingModels/ExportInvoicesModel.cs | 96 +++++++++++++++++++ .../Services/Invoices/InvoiceEntity.cs | 19 ++-- .../Views/Invoice/ListInvoices.cshtml | 11 ++- 4 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 BTCPayServer/Models/InvoicingModels/ExportInvoicesModel.cs diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs index f500bf914..71cbbdb96 100644 --- a/BTCPayServer/Controllers/InvoiceController.UI.cs +++ b/BTCPayServer/Controllers/InvoiceController.UI.cs @@ -57,7 +57,7 @@ namespace BTCPayServer.Controllers MonitoringDate = invoice.MonitoringExpiration, OrderId = invoice.OrderId, BuyerInformation = invoice.BuyerInformation, - Fiat = _CurrencyNameTable.DisplayFormatCurrency((decimal)dto.Price, dto.Currency), + Fiat = _CurrencyNameTable.DisplayFormatCurrency(dto.Price, dto.Currency), NotificationEmail = invoice.NotificationEmail, NotificationUrl = invoice.NotificationURL, RedirectUrl = invoice.RedirectURL, @@ -74,9 +74,9 @@ namespace BTCPayServer.Controllers var paymentMethodId = data.GetId(); var cryptoPayment = new InvoiceDetailsModel.CryptoPayment(); cryptoPayment.PaymentMethod = ToString(paymentMethodId); - cryptoPayment.Due = accounting.Due.ToString() + $" {paymentMethodId.CryptoCode}"; - cryptoPayment.Paid = accounting.CryptoPaid.ToString() + $" {paymentMethodId.CryptoCode}"; - cryptoPayment.Overpaid = (accounting.DueUncapped > Money.Zero ? Money.Zero : -accounting.DueUncapped).ToString() + $" {paymentMethodId.CryptoCode}"; + cryptoPayment.Due = $"{accounting.Due} {paymentMethodId.CryptoCode}"; + cryptoPayment.Paid = $"{accounting.CryptoPaid} {paymentMethodId.CryptoCode}"; + cryptoPayment.Overpaid = $"{accounting.OverpaidHelper} {paymentMethodId.CryptoCode}"; var onchainMethod = data.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod; if (onchainMethod != null) @@ -469,6 +469,20 @@ namespace BTCPayServer.Controllers return list; } + [HttpGet] + [Authorize(AuthenticationSchemes = Policies.CookieAuthentication)] + [BitpayAPIConstraint(false)] + public async Task Export(string format, string searchTerm = null) + { + var model = new ExportInvoicesModel(); + + var invoices = await ListInvoicesProcess(searchTerm, 0, int.MaxValue); + var res = model.Process(invoices, format); + return Content(res, "application/" + format); + } + + + [HttpGet] [Route("invoices/create")] [Authorize(AuthenticationSchemes = Policies.CookieAuthentication)] diff --git a/BTCPayServer/Models/InvoicingModels/ExportInvoicesModel.cs b/BTCPayServer/Models/InvoicingModels/ExportInvoicesModel.cs new file mode 100644 index 000000000..85f5f8a23 --- /dev/null +++ b/BTCPayServer/Models/InvoicingModels/ExportInvoicesModel.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BTCPayServer.Payments.Bitcoin; +using BTCPayServer.Services.Invoices; +using Newtonsoft.Json; + +namespace BTCPayServer.Models.InvoicingModels +{ + public class ExportInvoicesModel + { + 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(); + foreach (var i in invoices) + { + csvInvoices.AddRange(convertFromDb(i)); + } + + var serializerSett = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; + var json = JsonConvert.SerializeObject(csvInvoices, Formatting.Indented, serializerSett); + + return json; + } + + private IEnumerable convertFromDb(InvoiceEntity invoice) + { + var exportList = new List(); + // in this first version we are only exporting invoices that were paid + foreach (var payment in invoice.GetPayments()) + { + var cryptoCode = payment.GetPaymentMethodId().CryptoCode; + var pdata = payment.GetCryptoPaymentData(); + + var pmethod = invoice.GetPaymentMethod(payment.GetPaymentMethodId(), null); + var accounting = pmethod.Calculate(); + var onchainDetails = pmethod.GetPaymentMethodDetails() as BitcoinLikeOnChainPaymentMethod; + + var target = new ExportInvoiceHolder + { + PaymentId = pdata.GetPaymentId(), + CryptoCode = cryptoCode, + ConversionRate = pmethod.Rate, + Address = onchainDetails?.DepositAddress, + PaymentDue = $"{accounting.MinimumTotalDue} {cryptoCode}", + PaymentPaid = $"{accounting.CryptoPaid} {cryptoCode}", + PaymentOverpaid = $"{accounting.OverpaidHelper} {cryptoCode}", + + InvoiceId = invoice.Id, + CreatedDate = invoice.InvoiceTime.UtcDateTime, + ExpirationDate = invoice.ExpirationTime.UtcDateTime, + MonitoringDate = invoice.MonitoringExpiration.UtcDateTime, + Status = invoice.Status, + ItemCode = invoice.ProductInformation?.ItemCode, + ItemDesc = invoice.ProductInformation?.ItemDesc, + FiatPrice = invoice.ProductInformation?.Price ?? 0, + FiatCurrency = invoice.ProductInformation?.Currency, + }; + + exportList.Add(target); + } + + return exportList; + } + } + + public class ExportInvoiceHolder + { + public string PaymentId { get; set; } + public string CryptoCode { get; set; } + public decimal ConversionRate { get; set; } + public string Address { get; set; } + public string PaymentDue { get; set; } + public string PaymentPaid { get; set; } + public string PaymentOverpaid { get; set; } + + public string InvoiceId { get; set; } + public DateTime CreatedDate { get; set; } + public DateTime ExpirationDate { get; set; } + public DateTime MonitoringDate { get; set; } + public string Status { get; set; } + public string ItemCode { get; set; } + public string ItemDesc { get; set; } + public decimal FiatPrice { get; set; } + public string FiatCurrency { get; set; } + } +} diff --git a/BTCPayServer/Services/Invoices/InvoiceEntity.cs b/BTCPayServer/Services/Invoices/InvoiceEntity.cs index 72bbbb489..9adec9791 100644 --- a/BTCPayServer/Services/Invoices/InvoiceEntity.cs +++ b/BTCPayServer/Services/Invoices/InvoiceEntity.cs @@ -533,20 +533,21 @@ namespace BTCPayServer.Services.Invoices public class PaymentMethodAccounting { - /// - /// Total amount of this invoice - /// + /// Total amount of this invoice public Money TotalDue { get; set; } - /// - /// Amount of crypto remaining to pay this invoice - /// + /// Amount of crypto remaining to pay this invoice public Money Due { get; set; } - /// - /// Same as Due, can be negative - /// + /// Same as Due, can be negative public Money DueUncapped { get; set; } + + /// If DueUncapped is negative, that means user overpaid invoice + public Money OverpaidHelper + { + get { return DueUncapped > Money.Zero ? Money.Zero : -DueUncapped; } + } + /// /// Total amount of the invoice paid after conversion to this crypto currency /// diff --git a/BTCPayServer/Views/Invoice/ListInvoices.cshtml b/BTCPayServer/Views/Invoice/ListInvoices.cshtml index 3ca994084..803c10325 100644 --- a/BTCPayServer/Views/Invoice/ListInvoices.cshtml +++ b/BTCPayServer/Views/Invoice/ListInvoices.cshtml @@ -41,11 +41,18 @@