Remove period concept from PullPayment (#5963)

This commit is contained in:
Nicolas Dorier 2024-05-01 17:59:10 +09:00 committed by GitHub
parent 9db9c5e936
commit b4cd74056e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 66 additions and 155 deletions

View file

@ -12,8 +12,6 @@ namespace BTCPayServer.Client.Models
[JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))]
public decimal Amount { get; set; }
public string Currency { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
public TimeSpan? Period { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Days))]
[JsonProperty("BOLT11Expiration")]
public TimeSpan? BOLT11Expiration { get; set; }

View file

@ -24,8 +24,6 @@ namespace BTCPayServer.Client.Models
public string Currency { get; set; }
[JsonConverter(typeof(NumericStringJsonConverter))]
public decimal Amount { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
public TimeSpan? Period { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Days))]
[JsonProperty("BOLT11Expiration")]
public TimeSpan BOLT11Expiration { get; set; }

View file

@ -54,17 +54,5 @@ namespace BTCPayServer.Data
.Property(o => o.Proof)
.HasColumnType("JSONB");
}
// utility methods
public bool IsInPeriod(PullPaymentData pp, DateTimeOffset now)
{
var period = pp.GetPeriod(now);
if (period is { } p)
{
return p.Start <= Date && (p.End is not { } end || Date < end);
}
return false;
}
}
}

View file

@ -4,11 +4,13 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using NBitcoin;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace BTCPayServer.Data
{
@ -22,7 +24,6 @@ namespace BTCPayServer.Data
public StoreData StoreData { get; set; }
[MaxLength(50)]
public string StoreId { get; set; }
public long? Period { get; set; }
public DateTimeOffset StartDate { get; set; }
public DateTimeOffset? EndDate { get; set; }
public bool Archived { get; set; }
@ -49,27 +50,25 @@ namespace BTCPayServer.Data
return null;
if (EndDate is DateTimeOffset end && now >= end)
return null;
DateTimeOffset startPeriod = StartDate;
DateTimeOffset? endPeriod = null;
if (Period is long periodSeconds)
{
var period = TimeSpan.FromSeconds(periodSeconds);
var timeToNow = now - StartDate;
var periodCount = (long)timeToNow.TotalSeconds / (long)period.TotalSeconds;
startPeriod = StartDate + (period * periodCount);
endPeriod = startPeriod + period;
}
if (EndDate is DateTimeOffset end2 &&
((endPeriod is null) ||
(endPeriod is DateTimeOffset endP && endP > end2)))
endPeriod = end2;
return (startPeriod, endPeriod);
return (StartDate, EndDate);
}
public bool HasStarted()
{
return HasStarted(DateTimeOffset.UtcNow);
}
public TimeSpan? EndsIn() => EndsIn(DateTimeOffset.UtcNow);
public TimeSpan? EndsIn(DateTimeOffset now)
{
if (EndDate is DateTimeOffset e)
{
var resetIn = (e - now);
if (resetIn < TimeSpan.Zero)
resetIn = TimeSpan.Zero;
return resetIn;
}
return null;
}
public bool HasStarted(DateTimeOffset now)
{
return StartDate <= now;
@ -97,32 +96,6 @@ namespace BTCPayServer.Data
public static class PayoutExtensions
{
public static IQueryable<PayoutData> GetPayoutInPeriod(this IQueryable<PayoutData> payouts, PullPaymentData pp)
{
return GetPayoutInPeriod(payouts, pp, DateTimeOffset.UtcNow);
}
public static IQueryable<PayoutData> GetPayoutInPeriod(this IQueryable<PayoutData> payouts, PullPaymentData pp, DateTimeOffset now)
{
var request = payouts.Where(p => p.PullPaymentDataId == pp.Id);
var period = pp.GetPeriod(now);
if (period is { } p)
{
var start = p.Start;
if (p.End is DateTimeOffset end)
{
return request.Where(p => p.Date >= start && p.Date < end);
}
else
{
return request.Where(p => p.Date >= start);
}
}
else
{
return request.Where(p => false);
}
}
public static string GetStateString(this PayoutState state)
{
switch (state)

View file

@ -0,0 +1,31 @@
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240501015052_noperiod")]
public partial class noperiod : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Period",
table: "PullPayments");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<long>(
name: "Period",
table: "PullPayments",
type: "bigint",
nullable: true);
}
}
}

View file

@ -675,9 +675,6 @@ namespace BTCPayServer.Migrations
b.Property<DateTimeOffset?>("EndDate")
.HasColumnType("timestamp with time zone");
b.Property<long?>("Period")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("StartDate")
.HasColumnType("timestamp with time zone");

View file

@ -717,43 +717,6 @@ namespace BTCPayServer.Tests
Assert.Equal(0.0000_0001m, accounting.MinimumTotalDue);
}
[Fact]
public void CanCalculatePeriod()
{
Data.PullPaymentData data = new Data.PullPaymentData();
data.StartDate = Date(0);
data.EndDate = null;
var period = data.GetPeriod(Date(1)).Value;
Assert.Equal(Date(0), period.Start);
Assert.Null(period.End);
data.EndDate = Date(7);
period = data.GetPeriod(Date(1)).Value;
Assert.Equal(Date(0), period.Start);
Assert.Equal(Date(7), period.End);
data.Period = (long)TimeSpan.FromDays(2).TotalSeconds;
period = data.GetPeriod(Date(1)).Value;
Assert.Equal(Date(0), period.Start);
Assert.Equal(Date(2), period.End);
period = data.GetPeriod(Date(2)).Value;
Assert.Equal(Date(2), period.Start);
Assert.Equal(Date(4), period.End);
period = data.GetPeriod(Date(6)).Value;
Assert.Equal(Date(6), period.Start);
Assert.Equal(Date(7), period.End);
Assert.Null(data.GetPeriod(Date(7)));
Assert.Null(data.GetPeriod(Date(8)));
data.EndDate = null;
period = data.GetPeriod(Date(6)).Value;
Assert.Equal(Date(6), period.Start);
Assert.Equal(Date(8), period.End);
Assert.Null(data.GetPeriod(Date(-1)));
}
private DateTimeOffset Date(int days)
{
return new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero) + TimeSpan.FromDays(days);
}
[Fact]
public void CanDetectFileType()
{

View file

@ -872,7 +872,6 @@ namespace BTCPayServer.Tests
{
Assert.Equal("Test", result.Name);
Assert.Equal("Test description", result.Description);
Assert.Null(result.Period);
// If it contains ? it means that we are resolving an unknown route with the link generator
Assert.DoesNotContain("?", result.ViewLink);
Assert.False(result.Archived);

View file

@ -567,7 +567,6 @@ namespace BTCPayServer.Controllers.Greenfield
Name = ppBlob.Name,
Description = ppBlob.Description,
Currency = ppBlob.Currency,
Period = ppBlob.Period,
Archived = pp.Archived,
AutoApproveClaims = ppBlob.AutoApproveClaims,
BOLT11Expiration = ppBlob.BOLT11Expiration,

View file

@ -127,10 +127,6 @@ namespace BTCPayServer.Controllers.Greenfield
{
ModelState.AddModelError(nameof(request.ExpiresAt), $"expiresAt should be higher than startAt");
}
if (request.Period <= TimeSpan.Zero)
{
ModelState.AddModelError(nameof(request.Period), $"The period should be positive");
}
if (request.BOLT11Expiration < TimeSpan.Zero)
{
ModelState.AddModelError(nameof(request.BOLT11Expiration), $"The BOLT11 expiration should be positive");
@ -162,7 +158,6 @@ namespace BTCPayServer.Controllers.Greenfield
{
StartsAt = request.StartsAt,
ExpiresAt = request.ExpiresAt,
Period = request.Period,
BOLT11Expiration = request.BOLT11Expiration,
Name = request.Name,
Description = request.Description,
@ -188,7 +183,6 @@ namespace BTCPayServer.Controllers.Greenfield
Name = ppBlob.Name,
Description = ppBlob.Description,
Currency = ppBlob.Currency,
Period = ppBlob.Period,
Archived = pp.Archived,
AutoApproveClaims = ppBlob.AutoApproveClaims,
BOLT11Expiration = ppBlob.BOLT11Expiration,

View file

@ -86,7 +86,7 @@ namespace BTCPayServer.Controllers
return NotFound();
var storeBlob = store.GetStoreBlob();
var payouts = (await ctx.Payouts.GetPayoutInPeriod(pp)
var payouts = (await ctx.Payouts.Where(p => p.PullPaymentDataId == pp.Id)
.OrderByDescending(o => o.Date)
.ToListAsync())
.Select(o => new

View file

@ -19,8 +19,6 @@ namespace BTCPayServer.Data
[JsonConverter(typeof(NumericStringJsonConverter))]
public decimal MinimumClaim { get; set; }
public PullPaymentView View { get; set; } = new PullPaymentView();
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
public TimeSpan? Period { get; set; }
[DefaultValue(typeof(TimeSpan), "30.00:00:00")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]

View file

@ -41,7 +41,6 @@ namespace BTCPayServer.HostedServices
public string CustomCSSLink { get; set; }
public string EmbeddedCSS { get; set; }
public PayoutMethodId[] PayoutMethodIds { get; set; }
public TimeSpan? Period { get; set; }
public bool AutoApproveClaims { get; set; }
public TimeSpan? BOLT11Expiration { get; set; }
}
@ -121,7 +120,6 @@ namespace BTCPayServer.HostedServices
? date
: DateTimeOffset.UtcNow - TimeSpan.FromSeconds(1.0);
o.EndDate = create.ExpiresAt is DateTimeOffset date2 ? new DateTimeOffset?(date2) : null;
o.Period = create.Period is TimeSpan period ? (long?)period.TotalSeconds : null;
o.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20));
o.StoreId = create.StoreId;
@ -131,7 +129,6 @@ namespace BTCPayServer.HostedServices
Description = create.Description ?? string.Empty,
Currency = create.Currency,
Limit = create.Amount,
Period = o.Period is long periodSeconds ? (TimeSpan?)TimeSpan.FromSeconds(periodSeconds) : null,
SupportedPaymentMethods = create.PayoutMethodIds,
AutoApproveClaims = create.AutoApproveClaims,
View = new PullPaymentBlob.PullPaymentView()
@ -608,7 +605,7 @@ namespace BTCPayServer.HostedServices
var payoutsRaw = withoutPullPayment
? null
: await ctx.Payouts.GetPayoutInPeriod(pp, now)
: await ctx.Payouts.Where(p => p.PullPaymentDataId == pp.Id)
.Where(p => p.State != PayoutState.Cancelled).ToListAsync();
var payouts = payoutsRaw?.Select(o => new { Entity = o, Blob = o.GetBlob(_jsonSerializerSettings) });
@ -801,15 +798,15 @@ namespace BTCPayServer.HostedServices
var ni = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
var nfi = _currencyNameTable.GetNumberFormatInfo(ppBlob.Currency, true);
var totalCompleted = pp.Payouts.Where(p => (p.State == PayoutState.Completed ||
p.State == PayoutState.InProgress) && p.IsInPeriod(pp, now))
var totalCompleted = pp.Payouts
.Where(p => (p.State == PayoutState.Completed ||
p.State == PayoutState.InProgress))
.Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum().RoundToSignificant(ni.Divisibility);
var period = pp.GetPeriod(now);
var totalAwaiting = pp.Payouts.Where(p => (p.State == PayoutState.AwaitingPayment ||
p.State == PayoutState.AwaitingApproval) &&
p.IsInPeriod(pp, now)).Select(o =>
var totalAwaiting = pp.Payouts
.Where(p => (p.State == PayoutState.AwaitingPayment ||
p.State == PayoutState.AwaitingApproval)).Select(o =>
o.GetBlob(_jsonSerializerSettings).Amount).Sum().RoundToSignificant(ni.Divisibility);
;
var currencyData = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
return new PullPaymentsModel.PullPaymentModel.ProgressModel()
{
@ -821,8 +818,7 @@ namespace BTCPayServer.HostedServices
CompletedFormatted = totalCompleted.ToString("C", nfi),
Limit = ppBlob.Limit.RoundToSignificant(currencyData.Divisibility),
LimitFormatted = _displayFormatter.Currency(ppBlob.Limit, ppBlob.Currency),
ResetIn = period?.End is { } nr ? ZeroIfNegative(nr - now).TimeString() : null,
EndIn = pp.EndDate is { } end ? ZeroIfNegative(end - now).TimeString() : null,
EndIn = pp.EndsIn() is { } end ? end.TimeString() : null,
};
}

View file

@ -35,17 +35,16 @@ namespace BTCPayServer.Models
ExpiryDate = data.EndDate is DateTimeOffset dt ? (DateTime?)dt.UtcDateTime : null;
Email = blob.View.Email;
MinimumClaim = blob.MinimumClaim;
IsPending = !data.IsExpired();
var period = data.GetPeriod(now);
IsPending = !data.IsExpired(now);
if (data.Archived)
{
Status = "Archived";
}
else if (data.IsExpired())
else if (data.IsExpired(now))
{
Status = "Expired";
}
else if (period is null)
else if (!data.HasStarted(now))
{
Status = "Not yet started";
}
@ -54,13 +53,10 @@ namespace BTCPayServer.Models
Status = string.Empty;
}
ResetIn = string.Empty;
if (period?.End is DateTimeOffset pe)
EndsIn = string.Empty;
if (data.EndsIn(now) is TimeSpan e)
{
var resetIn = (pe - DateTimeOffset.UtcNow);
if (resetIn < TimeSpan.Zero)
resetIn = TimeSpan.Zero;
ResetIn = resetIn.TimeString();
EndsIn = e.TimeString();
}
}
@ -76,7 +72,7 @@ namespace BTCPayServer.Models
public string ResetDeepLink { get; set; }
public string HubPath { get; set; }
public string ResetIn { get; set; }
public string EndsIn { get; set; }
public string Email { get; set; }
public string Status { get; set; }
public bool IsPending { get; set; }

View file

@ -20,7 +20,6 @@ namespace BTCPayServer.Models.WalletViewModels
public string CompletedFormatted { get; set; }
public string AwaitingFormatted { get; set; }
public string LimitFormatted { get; set; }
public string ResetIn { get; set; }
public string EndIn { get; set; }
public decimal Awaiting { get; set; }
public decimal Completed { get; set; }

View file

@ -111,12 +111,12 @@
<span class="fa fa-qrcode"></span> Show QR
</button>
</div>
@if (!string.IsNullOrEmpty(Model.ResetIn))
@if (!string.IsNullOrEmpty(Model.EndsIn))
{
<p>
<span class="text-muted text-nowrap">Reset in</span>
<span class="text-muted text-nowrap">Ends in</span>
&nbsp;
<span class="text-nowrap">@Model.ResetIn</span>
<span class="text-nowrap">@Model.EndsIn</span>
</p>
}
@if (!string.IsNullOrEmpty(Model.Description))

View file

@ -87,11 +87,6 @@
<span>Completed:&nbsp;<span class="float-end">@pp.Progress.CompletedFormatted</span></span>
<br />
<span>Limit:&nbsp;<span class="float-end">@pp.Progress.LimitFormatted</span></span>
@if (pp.Progress.ResetIn != null)
{
<br />
<span>Resets in:&nbsp;<span class="float-end">@pp.Progress.ResetIn</span></span>
}
@if (pp.Progress.EndIn != null)
{
<br />

View file

@ -195,13 +195,6 @@
"example": "BTC",
"description": "The currency of the amount."
},
"period": {
"type": "integer",
"format": "decimal",
"example": 604800,
"nullable": true,
"description": "The length of each period in seconds."
},
"BOLT11Expiration": {
"type": "string",
"example": 30,
@ -1179,12 +1172,6 @@
"example": "1.12000000",
"description": "The amount in the currency of this pull payment as a decimal string"
},
"period": {
"type": "integer",
"example": 604800,
"nullable": true,
"description": "The length of each period in seconds"
},
"BOLT11Expiration": {
"type": "string",
"example": 30,