mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-10 00:09:18 +01:00
Remove period concept from PullPayment (#5963)
This commit is contained in:
parent
9db9c5e936
commit
b4cd74056e
18 changed files with 66 additions and 155 deletions
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
31
BTCPayServer.Data/Migrations/20240501015052_noperiod.cs
Normal file
31
BTCPayServer.Data/Migrations/20240501015052_noperiod.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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>
|
||||
|
||||
<span class="text-nowrap">@Model.ResetIn</span>
|
||||
<span class="text-nowrap">@Model.EndsIn</span>
|
||||
</p>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.Description))
|
||||
|
|
|
@ -87,11 +87,6 @@
|
|||
<span>Completed: <span class="float-end">@pp.Progress.CompletedFormatted</span></span>
|
||||
<br />
|
||||
<span>Limit: <span class="float-end">@pp.Progress.LimitFormatted</span></span>
|
||||
@if (pp.Progress.ResetIn != null)
|
||||
{
|
||||
<br />
|
||||
<span>Resets in: <span class="float-end">@pp.Progress.ResetIn</span></span>
|
||||
}
|
||||
@if (pp.Progress.EndIn != null)
|
||||
{
|
||||
<br />
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue