Skip to content

Commit

Permalink
Add refund reports
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasDorier committed Apr 19, 2024
1 parent f4aafd5 commit 62b5441
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
1 change: 1 addition & 0 deletions BTCPayServer/Hosting/BTCPayServerServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ public static IServiceCollection AddBTCPayServer(this IServiceCollection service
services.AddReportProvider<ProductsReportProvider>();
services.AddReportProvider<PayoutsReportProvider>();
services.AddReportProvider<LegacyInvoiceExportReportProvider>();
services.AddReportProvider<RefundsReportProvider>();
services.AddWebhooks();
services.AddSingleton<BitcoinLikePayoutHandler>();
services.AddSingleton<IPayoutHandler>(provider => provider.GetRequiredService<BitcoinLikePayoutHandler>());
Expand Down
132 changes: 132 additions & 0 deletions BTCPayServer/Services/Reporting/RefundsReportProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using Dapper;
using Microsoft.EntityFrameworkCore;
using MySqlConnector;

namespace BTCPayServer.Services.Reporting
{
public class RefundsReportProvider : ReportProvider
{
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
private readonly DisplayFormatter _displayFormatter;

private ViewDefinition CreateDefinition()
{
return new ViewDefinition
{
Fields = new List<StoreReportResponse.Field>
{
new("Date", "datetime"),
new("InvoiceId", "invoice_id"),
new("Currency", "string"),
new("Paid", "amount"),
new("Pending", "amount"),
new("FullyPaid", "boolean")
},
Charts =
{
new ()
{
Name = "Aggregated amount",
Groups = { "Currency" },
HasGrandTotal = false,
Aggregates = { "Paid", "Pending" }
}
}
};
}
public override string Name => "Refunds";

public ApplicationDbContextFactory DbContextFactory { get; }

public RefundsReportProvider(
ApplicationDbContextFactory dbContextFactory,
BTCPayNetworkJsonSerializerSettings serializerSettings,
DisplayFormatter displayFormatter)
{
DbContextFactory = dbContextFactory;
_serializerSettings = serializerSettings;
_displayFormatter = displayFormatter;
}
record RefundRow(DateTimeOffset Created, string InvoiceId, string PullPaymentId, string Currency, decimal Limit)
{
public decimal PaidAmount { get; set; }
public decimal PendingAmount { get; set; }
}
public override async Task Query(QueryContext queryContext, CancellationToken cancellation)
{
queryContext.ViewDefinition = CreateDefinition();
RefundRow? currentRow = null;
await using var ctx = DbContextFactory.CreateContext();
var conn = ctx.Database.GetDbConnection();
var rows = await conn.QueryAsync(
"""
SELECT i."Created", i."Id" AS "InvoiceId", p."State", p."PaymentMethodId", pp."Id" AS "PullPaymentId", pp."Blob" AS "ppBlob", p."Blob" AS "pBlob" FROM "Invoices" i
JOIN "Refunds" r ON r."InvoiceDataId"= i."Id"
JOIN "PullPayments" pp ON r."PullPaymentDataId"=pp."Id"
LEFT JOIN "Payouts" p ON p."PullPaymentDataId"=pp."Id"
WHERE i."StoreDataId" = @storeId
AND i."Created" >= @start AND i."Created" <= @end
AND pp."Archived" IS FALSE
ORDER BY i."Created", pp."Id"
""", new { start = queryContext.From, end = queryContext.To, storeId = queryContext.StoreId });
foreach (var r in rows)
{
PullPaymentBlob ppBlob = GetPullPaymentBlob(r);
PayoutBlob? pBlob = GetPayoutBlob(r);

if ((string)r.PullPaymentId != currentRow?.PullPaymentId)
{
AddRow(queryContext, currentRow);
currentRow = new(r.Created, r.InvoiceId, r.PullPaymentId, ppBlob.Currency, ppBlob.Limit);
}
if (pBlob is null)
continue;
var state = Enum.Parse<PayoutState>((string)r.State);
if (state == PayoutState.Cancelled)
continue;
if (state is PayoutState.Completed)
currentRow.PaidAmount += pBlob.Amount;
else
currentRow.PendingAmount += pBlob.Amount;
}
AddRow(queryContext, currentRow);
}

private PayoutBlob? GetPayoutBlob(dynamic r)
{
if (r.pBlob is null)
return null;
Data.PayoutData p = new Data.PayoutData();
p.PaymentMethodId = r.PaymentMethodId;
p.Blob = (string)r.pBlob;
return p.GetBlob(_serializerSettings);
}

private static PullPaymentBlob GetPullPaymentBlob(dynamic r)
{
Data.PullPaymentData pp = new Data.PullPaymentData();
pp.Blob = (string)r.ppBlob;
return pp.GetBlob();
}

private void AddRow(QueryContext queryContext, RefundRow? currentRow)
{
if (currentRow is null)
return;
var data = queryContext.AddData();
data.Add(currentRow.Created);
data.Add(currentRow.InvoiceId);
data.Add(currentRow.Currency);
data.Add(_displayFormatter.ToFormattedAmount(currentRow.PaidAmount, currentRow.Currency));
data.Add(_displayFormatter.ToFormattedAmount(currentRow.PendingAmount, currentRow.Currency));
data.Add(currentRow.Limit <= currentRow.PaidAmount);
}
}
}

0 comments on commit 62b5441

Please sign in to comment.