mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Fix live update of crowdfunding, add tests, consider payments as confirmed if invoice is confirmed
This commit is contained in:
@@ -23,6 +23,7 @@ using NBitcoin;
|
|||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
using static BTCPayServer.Tests.UnitTest1;
|
||||||
|
|
||||||
namespace BTCPayServer.Tests
|
namespace BTCPayServer.Tests
|
||||||
{
|
{
|
||||||
@@ -183,7 +184,8 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
||||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model)
|
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model)
|
||||||
.Apps[0].Id;
|
.Apps[0].Id;
|
||||||
|
|
||||||
|
Logs.Tester.LogInformation("We create an invoice with a hardcap");
|
||||||
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
|
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
|
||||||
.IsType<ViewResult>(apps.UpdateCrowdfund(appId).Result).Model);
|
.IsType<ViewResult>(apps.UpdateCrowdfund(appId).Result).Model);
|
||||||
crowdfundViewModel.Enabled = true;
|
crowdfundViewModel.Enabled = true;
|
||||||
@@ -191,6 +193,7 @@ namespace BTCPayServer.Tests
|
|||||||
crowdfundViewModel.TargetAmount = 100;
|
crowdfundViewModel.TargetAmount = 100;
|
||||||
crowdfundViewModel.TargetCurrency = "BTC";
|
crowdfundViewModel.TargetCurrency = "BTC";
|
||||||
crowdfundViewModel.UseAllStoreInvoices = true;
|
crowdfundViewModel.UseAllStoreInvoices = true;
|
||||||
|
crowdfundViewModel.EnforceTargetAmount = true;
|
||||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result);
|
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result);
|
||||||
|
|
||||||
var anonAppPubsController = tester.PayTester.GetController<AppsPublicController>();
|
var anonAppPubsController = tester.PayTester.GetController<AppsPublicController>();
|
||||||
@@ -207,9 +210,10 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(0m, model.Info.CurrentAmount );
|
Assert.Equal(0m, model.Info.CurrentAmount );
|
||||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||||
Assert.Equal(0m, model.Info.ProgressPercentage);
|
Assert.Equal(0m, model.Info.ProgressPercentage);
|
||||||
|
|
||||||
|
|
||||||
|
Logs.Tester.LogInformation("Unpaid invoices should show as pending contribution because it is hardcap");
|
||||||
|
Logs.Tester.LogInformation("Because UseAllStoreInvoices is true, we can manually create an invoice and it should show as contribution");
|
||||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
{
|
{
|
||||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||||
@@ -225,24 +229,32 @@ namespace BTCPayServer.Tests
|
|||||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model);
|
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model);
|
||||||
|
|
||||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
Assert.Equal(0m ,model.Info.CurrentAmount);
|
||||||
tester.ExplorerNode.SendToAddress(invoiceAddress,invoice.BtcDue);
|
|
||||||
Assert.Equal(0m ,model.Info.CurrentAmount );
|
|
||||||
Assert.Equal(1m, model.Info.CurrentPendingAmount);
|
Assert.Equal(1m, model.Info.CurrentPendingAmount);
|
||||||
Assert.Equal( 0m, model.Info.ProgressPercentage);
|
Assert.Equal(0m, model.Info.ProgressPercentage);
|
||||||
Assert.Equal(1m, model.Info.PendingProgressPercentage);
|
Assert.Equal(1m, model.Info.PendingProgressPercentage);
|
||||||
|
|
||||||
|
Logs.Tester.LogInformation("Let's check current amount change once payment is confirmed");
|
||||||
|
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
||||||
|
tester.ExplorerNode.SendToAddress(invoiceAddress, invoice.BtcDue);
|
||||||
|
tester.ExplorerNode.Generate(1); // By default invoice confirmed at 1 block
|
||||||
|
TestUtils.Eventually(() =>
|
||||||
|
{
|
||||||
|
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||||
|
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model);
|
||||||
|
Assert.Equal(1m, model.Info.CurrentAmount);
|
||||||
|
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
Logs.Tester.LogInformation("Because UseAllStoreInvoices is true, let's make sure the invoice is tagged");
|
||||||
var invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
var invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
||||||
Assert.True(invoiceEntity.Version >= InvoiceEntity.InternalTagSupport_Version);
|
Assert.True(invoiceEntity.Version >= InvoiceEntity.InternalTagSupport_Version);
|
||||||
Assert.Contains(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags);
|
Assert.Contains(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags);
|
||||||
|
|
||||||
crowdfundViewModel.Enabled = true;
|
|
||||||
crowdfundViewModel.EndDate = null;
|
|
||||||
crowdfundViewModel.TargetAmount = 100;
|
|
||||||
crowdfundViewModel.TargetCurrency = "BTC";
|
|
||||||
crowdfundViewModel.UseAllStoreInvoices = false;
|
crowdfundViewModel.UseAllStoreInvoices = false;
|
||||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result);
|
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result);
|
||||||
|
|
||||||
|
Logs.Tester.LogInformation("Because UseAllStoreInvoices is false, let's make sure the invoice is not tagged");
|
||||||
invoice = user.BitPay.CreateInvoice(new Invoice()
|
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
{
|
{
|
||||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||||
@@ -255,6 +267,30 @@ namespace BTCPayServer.Tests
|
|||||||
}, Facade.Merchant);
|
}, Facade.Merchant);
|
||||||
invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
||||||
Assert.DoesNotContain(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags);
|
Assert.DoesNotContain(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags);
|
||||||
|
|
||||||
|
Logs.Tester.LogInformation("After turning setting a softcap, let's check that only actual payments are counted");
|
||||||
|
crowdfundViewModel.EnforceTargetAmount = false;
|
||||||
|
crowdfundViewModel.UseAllStoreInvoices = true;
|
||||||
|
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result);
|
||||||
|
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
|
{
|
||||||
|
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||||
|
Price = 1m,
|
||||||
|
Currency = "BTC",
|
||||||
|
PosData = "posData",
|
||||||
|
ItemDesc = "Some description",
|
||||||
|
TransactionSpeed = "high",
|
||||||
|
FullNotifications = true
|
||||||
|
}, Facade.Merchant);
|
||||||
|
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||||
|
invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
||||||
|
tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(0.5m));
|
||||||
|
TestUtils.Eventually(() =>
|
||||||
|
{
|
||||||
|
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||||
|
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model);
|
||||||
|
Assert.Equal(0.5m, model.Info.CurrentPendingAmount);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
public partial class AppsController
|
public partial class AppsController
|
||||||
{
|
{
|
||||||
public class CrowdfundAppUpdated
|
public class AppUpdated
|
||||||
{
|
{
|
||||||
public string AppId { get; set; }
|
public string AppId { get; set; }
|
||||||
public CrowdfundSettings Settings { get; set; }
|
public object Settings { get; set; }
|
||||||
public string StoreId { get; set; }
|
public string StoreId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +46,6 @@ namespace BTCPayServer.Controllers
|
|||||||
SoundsEnabled = settings.SoundsEnabled,
|
SoundsEnabled = settings.SoundsEnabled,
|
||||||
DisqusShortname = settings.DisqusShortname,
|
DisqusShortname = settings.DisqusShortname,
|
||||||
AnimationsEnabled = settings.AnimationsEnabled,
|
AnimationsEnabled = settings.AnimationsEnabled,
|
||||||
UseInvoiceAmount = settings.UseInvoiceAmount,
|
|
||||||
ResetEveryAmount = settings.ResetEveryAmount,
|
ResetEveryAmount = settings.ResetEveryAmount,
|
||||||
ResetEvery = Enum.GetName(typeof(CrowdfundResetEvery), settings.ResetEvery),
|
ResetEvery = Enum.GetName(typeof(CrowdfundResetEvery), settings.ResetEvery),
|
||||||
UseAllStoreInvoices = app.TagAllInvoices,
|
UseAllStoreInvoices = app.TagAllInvoices,
|
||||||
@@ -120,7 +119,6 @@ namespace BTCPayServer.Controllers
|
|||||||
AnimationsEnabled = vm.AnimationsEnabled,
|
AnimationsEnabled = vm.AnimationsEnabled,
|
||||||
ResetEveryAmount = vm.ResetEveryAmount,
|
ResetEveryAmount = vm.ResetEveryAmount,
|
||||||
ResetEvery = Enum.Parse<CrowdfundResetEvery>(vm.ResetEvery),
|
ResetEvery = Enum.Parse<CrowdfundResetEvery>(vm.ResetEvery),
|
||||||
UseInvoiceAmount = vm.UseInvoiceAmount,
|
|
||||||
DisplayPerksRanking = vm.DisplayPerksRanking,
|
DisplayPerksRanking = vm.DisplayPerksRanking,
|
||||||
SortPerksByPopularity = vm.SortPerksByPopularity
|
SortPerksByPopularity = vm.SortPerksByPopularity
|
||||||
};
|
};
|
||||||
@@ -129,7 +127,7 @@ namespace BTCPayServer.Controllers
|
|||||||
app.SetSettings(newSettings);
|
app.SetSettings(newSettings);
|
||||||
await UpdateAppSettings(app);
|
await UpdateAppSettings(app);
|
||||||
|
|
||||||
_EventAggregator.Publish(new CrowdfundAppUpdated()
|
_EventAggregator.Publish(new AppUpdated()
|
||||||
{
|
{
|
||||||
AppId = appId,
|
AppId = appId,
|
||||||
StoreId = app.StoreDataId,
|
StoreId = app.StoreDataId,
|
||||||
|
|||||||
@@ -106,11 +106,11 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
return NotFound("A Target Currency must be set for this app in order to be loadable.");
|
return NotFound("A Target Currency must be set for this app in order to be loadable.");
|
||||||
}
|
}
|
||||||
if (settings.Enabled) return View(await _AppService.GetCrowdfundInfo(appId));
|
if (settings.Enabled) return View(await _AppService.GetAppInfo(appId));
|
||||||
if(!isAdmin)
|
if(!isAdmin)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
return View(await _AppService.GetCrowdfundInfo(appId));
|
return View(await _AppService.GetAppInfo(appId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@@ -135,7 +135,7 @@ namespace BTCPayServer.Controllers
|
|||||||
return NotFound("Crowdfund is not currently active");
|
return NotFound("Crowdfund is not currently active");
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = await _AppService.GetCrowdfundInfo(appId);
|
var info = (ViewCrowdfundViewModel)await _AppService.GetAppInfo(appId);
|
||||||
|
|
||||||
if (!isAdmin &&
|
if (!isAdmin &&
|
||||||
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
|
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
|
||||||
|
|||||||
@@ -69,8 +69,6 @@ namespace BTCPayServer.Models.AppViewModels
|
|||||||
[Display(Name = "Custom CSS Code")]
|
[Display(Name = "Custom CSS Code")]
|
||||||
public string EmbeddedCSS { get; set; }
|
public string EmbeddedCSS { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Base the contributed goal amount on the invoice amount and not actual payments")]
|
|
||||||
public bool UseInvoiceAmount { get; set; }
|
|
||||||
[Display(Name = "Count all invoices created on the store as part of the crowdfunding goal")]
|
[Display(Name = "Count all invoices created on the store as part of the crowdfunding goal")]
|
||||||
public bool UseAllStoreInvoices { get; set; }
|
public bool UseAllStoreInvoices { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -27,15 +27,17 @@ namespace BTCPayServer.Services.Apps
|
|||||||
{
|
{
|
||||||
private readonly EventAggregator _EventAggregator;
|
private readonly EventAggregator _EventAggregator;
|
||||||
private readonly IHubContext<AppHub> _HubContext;
|
private readonly IHubContext<AppHub> _HubContext;
|
||||||
|
private readonly AppService _appService;
|
||||||
private List<IEventAggregatorSubscription> _Subscriptions;
|
private List<IEventAggregatorSubscription> _Subscriptions;
|
||||||
private CancellationTokenSource _Cts;
|
private CancellationTokenSource _Cts;
|
||||||
|
|
||||||
public AppHubStreamer(EventAggregator eventAggregator,
|
public AppHubStreamer(EventAggregator eventAggregator,
|
||||||
IHubContext<AppHub> hubContext)
|
IHubContext<AppHub> hubContext,
|
||||||
|
AppService appService)
|
||||||
{
|
{
|
||||||
_EventAggregator = eventAggregator;
|
_EventAggregator = eventAggregator;
|
||||||
_HubContext = hubContext;
|
_HubContext = hubContext;
|
||||||
|
_appService = appService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task NotifyClients(string appId, InvoiceEvent invoiceEvent, CancellationToken cancellationToken)
|
private async Task NotifyClients(string appId, InvoiceEvent invoiceEvent, CancellationToken cancellationToken)
|
||||||
@@ -51,19 +53,27 @@ namespace BTCPayServer.Services.Apps
|
|||||||
invoiceEvent.Payment.GetPaymentMethodId().PaymentType)
|
invoiceEvent.Payment.GetPaymentMethodId().PaymentType)
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
}
|
}
|
||||||
|
await InfoUpdated(appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel<InvoiceEvent> _InvoiceEvents = Channel.CreateUnbounded<InvoiceEvent>();
|
Channel<object> _Events = Channel.CreateUnbounded<object>();
|
||||||
public async Task ProcessEvents(CancellationToken cancellationToken)
|
public async Task ProcessEvents(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
while (await _InvoiceEvents.Reader.WaitToReadAsync(cancellationToken))
|
while (await _Events.Reader.WaitToReadAsync(cancellationToken))
|
||||||
{
|
{
|
||||||
if (_InvoiceEvents.Reader.TryRead(out var evt))
|
if (_Events.Reader.TryRead(out var evt))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach(var appId in AppService.GetAppInternalTags(evt.Invoice.InternalTags))
|
if (evt is InvoiceEvent invoiceEvent)
|
||||||
await NotifyClients(appId, evt, cancellationToken);
|
{
|
||||||
|
foreach (var appId in AppService.GetAppInternalTags(invoiceEvent.Invoice.InternalTags))
|
||||||
|
await NotifyClients(appId, invoiceEvent, cancellationToken);
|
||||||
|
}
|
||||||
|
else if (evt is AppsController.AppUpdated app)
|
||||||
|
{
|
||||||
|
await InfoUpdated(app.AppId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch when (cancellationToken.IsCancellationRequested)
|
catch when (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -77,11 +87,18 @@ namespace BTCPayServer.Services.Apps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task InfoUpdated(string appId)
|
||||||
|
{
|
||||||
|
var info = await _appService.GetAppInfo(appId);
|
||||||
|
await _HubContext.Clients.Group(appId).SendCoreAsync(AppHub.InfoUpdated, new object[] { info });
|
||||||
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_Subscriptions = new List<IEventAggregatorSubscription>()
|
_Subscriptions = new List<IEventAggregatorSubscription>()
|
||||||
{
|
{
|
||||||
_EventAggregator.Subscribe<InvoiceEvent>(e => _InvoiceEvents.Writer.TryWrite(e))
|
_EventAggregator.Subscribe<InvoiceEvent>(e => _Events.Writer.TryWrite(e)),
|
||||||
|
_EventAggregator.Subscribe<AppsController.AppUpdated>(e => _Events.Writer.TryWrite(e))
|
||||||
};
|
};
|
||||||
_Cts = new CancellationTokenSource();
|
_Cts = new CancellationTokenSource();
|
||||||
_ProcessingEvents = ProcessEvents(_Cts.Token);
|
_ProcessingEvents = ProcessEvents(_Cts.Token);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace BTCPayServer.Services.Apps
|
|||||||
_Networks = networks;
|
_Networks = networks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ViewCrowdfundViewModel> GetCrowdfundInfo(string appId)
|
public async Task<object> GetAppInfo(string appId)
|
||||||
{
|
{
|
||||||
var app = await GetApp(appId, AppType.Crowdfund, true);
|
var app = await GetApp(appId, AppType.Crowdfund, true);
|
||||||
return await GetInfo(app);
|
return await GetInfo(app);
|
||||||
@@ -91,13 +91,13 @@ namespace BTCPayServer.Services.Apps
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invoices = await GetInvoicesForApp(appData, lastResetDate);
|
var invoices = await GetInvoicesForApp(appData, lastResetDate);
|
||||||
var completeInvoices = invoices.Where(entity => entity.Status == InvoiceStatus.Complete).ToArray();
|
var completeInvoices = invoices.Where(entity => entity.Status == InvoiceStatus.Complete || entity.Status == InvoiceStatus.Confirmed).ToArray();
|
||||||
var pendingInvoices = invoices.Where(entity => entity.Status != InvoiceStatus.Complete).ToArray();
|
var pendingInvoices = invoices.Where(entity => !(entity.Status == InvoiceStatus.Complete || entity.Status == InvoiceStatus.Confirmed)).ToArray();
|
||||||
|
|
||||||
var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(_Networks);
|
var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(_Networks);
|
||||||
|
|
||||||
var pendingPaymentStats = GetCurrentContributionAmountStats(pendingInvoices, !settings.UseInvoiceAmount);
|
var pendingPaymentStats = GetCurrentContributionAmountStats(pendingInvoices, !settings.EnforceTargetAmount);
|
||||||
var paymentStats = GetCurrentContributionAmountStats(completeInvoices, !settings.UseInvoiceAmount);
|
var paymentStats = GetCurrentContributionAmountStats(completeInvoices, !settings.EnforceTargetAmount);
|
||||||
|
|
||||||
var currentAmount = await GetCurrentContributionAmount(
|
var currentAmount = await GetCurrentContributionAmount(
|
||||||
paymentStats,
|
paymentStats,
|
||||||
@@ -106,9 +106,6 @@ namespace BTCPayServer.Services.Apps
|
|||||||
pendingPaymentStats,
|
pendingPaymentStats,
|
||||||
settings.TargetCurrency, rateRules);
|
settings.TargetCurrency, rateRules);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var perkCount = invoices
|
var perkCount = invoices
|
||||||
.Where(entity => !string.IsNullOrEmpty(entity.ProductInformation.ItemCode))
|
.Where(entity => !string.IsNullOrEmpty(entity.ProductInformation.ItemCode))
|
||||||
.GroupBy(entity => entity.ProductInformation.ItemCode)
|
.GroupBy(entity => entity.ProductInformation.ItemCode)
|
||||||
@@ -325,25 +322,35 @@ namespace BTCPayServer.Services.Apps
|
|||||||
return result.Sum();
|
return result.Sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, decimal> GetCurrentContributionAmountStats(InvoiceEntity[] invoices, bool usePaymentData = true)
|
public Dictionary<string, decimal> GetCurrentContributionAmountStats(InvoiceEntity[] invoices, bool softcap)
|
||||||
{
|
{
|
||||||
if (usePaymentData)
|
return invoices
|
||||||
{
|
.SelectMany(p =>
|
||||||
var payments = invoices.SelectMany(entity => entity.GetPayments());
|
{
|
||||||
|
// For hardcap, we count newly created invoices as part of the contributions
|
||||||
|
if (!softcap && p.Status == InvoiceStatus.New)
|
||||||
|
return new[] { (Key: p.ProductInformation.Currency, Value: p.ProductInformation.Price) };
|
||||||
|
|
||||||
var groupedByMethod = payments.GroupBy(entity => entity.GetPaymentMethodId());
|
// If the user get a donation via other mean, he can register an invoice manually for such amount
|
||||||
|
// then mark the invoice as complete
|
||||||
|
var payments = p.GetPayments();
|
||||||
|
if (payments.Count == 0 &&
|
||||||
|
p.ExceptionStatus == InvoiceExceptionStatus.Marked &&
|
||||||
|
p.Status == InvoiceStatus.Complete)
|
||||||
|
return new[] { (Key: p.ProductInformation.Currency, Value: p.ProductInformation.Price) };
|
||||||
|
|
||||||
return groupedByMethod.ToDictionary(entities => entities.Key.ToString(),
|
// If an invoice has been marked invalid, remove the contribution
|
||||||
entities => entities.Sum(entity => entity.GetCryptoPaymentData().GetValue()));
|
if (p.ExceptionStatus == InvoiceExceptionStatus.Marked &&
|
||||||
}
|
p.Status == InvoiceStatus.Invalid)
|
||||||
else
|
return new[] { (Key: p.ProductInformation.Currency, Value: 0m) };
|
||||||
{
|
|
||||||
return invoices
|
// Else, we just sum the payments
|
||||||
.GroupBy(entity => entity.ProductInformation.Currency)
|
return payments
|
||||||
.ToDictionary(
|
.GroupBy(pay => pay.GetPaymentMethodId())
|
||||||
entities => entities.Key,
|
.Select(payGroup => (Key: payGroup.Key.ToString(),
|
||||||
entities => entities.Sum(entity => entity.ProductInformation.Price));
|
Value: payGroup.Select(pay => pay.GetCryptoPaymentData().GetValue()).Sum())).ToArray();
|
||||||
}
|
})
|
||||||
|
.ToDictionary(p => p.Key, p => p.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PosHolder
|
private class PosHolder
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ namespace BTCPayServer.Services.Apps
|
|||||||
public bool SoundsEnabled { get; set; } = true;
|
public bool SoundsEnabled { get; set; } = true;
|
||||||
public string DisqusShortname { get; set; }
|
public string DisqusShortname { get; set; }
|
||||||
public bool AnimationsEnabled { get; set; } = true;
|
public bool AnimationsEnabled { get; set; } = true;
|
||||||
public bool UseInvoiceAmount { get; set; } = true;
|
|
||||||
public int ResetEveryAmount { get; set; } = 1;
|
public int ResetEveryAmount { get; set; } = 1;
|
||||||
public CrowdfundResetEvery ResetEvery { get; set; } = CrowdfundResetEvery.Never;
|
public CrowdfundResetEvery ResetEvery { get; set; } = CrowdfundResetEvery.Never;
|
||||||
[Obsolete("Use AppData.TagAllInvoices instead")]
|
[Obsolete("Use AppData.TagAllInvoices instead")]
|
||||||
|
|||||||
@@ -157,11 +157,6 @@
|
|||||||
<input asp-for="EnforceTargetAmount" type="checkbox" class="form-check"/>
|
<input asp-for="EnforceTargetAmount" type="checkbox" class="form-check"/>
|
||||||
<span asp-validation-for="EnforceTargetAmount" class="text-danger"></span>
|
<span asp-validation-for="EnforceTargetAmount" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="UseInvoiceAmount"></label>
|
|
||||||
<input asp-for="UseInvoiceAmount" type="checkbox" class="form-check"/>
|
|
||||||
<span asp-validation-for="UseInvoiceAmount" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="UseAllStoreInvoices"></label>
|
<label asp-for="UseAllStoreInvoices"></label>
|
||||||
<input asp-for="UseAllStoreInvoices" type="checkbox" class="form-check"/>
|
<input asp-for="UseAllStoreInvoices" type="checkbox" class="form-check"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user