mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 14:04:26 +01:00
Introduce Server paging for Payouts List (#2564)
* Introduce Server paging for Payouts List * Add paging params * Minor code and formatting improvements * View updates * Apply suggestions from code review Co-authored-by: Zaxounette <51208677+Zaxounette@users.noreply.github.com> * fix tests Co-authored-by: Dennis Reimann <mail@dennisreimann.de> Co-authored-by: Zaxounette <51208677+Zaxounette@users.noreply.github.com>
This commit is contained in:
@@ -912,8 +912,7 @@ namespace BTCPayServer.Tests
|
|||||||
[Trait("Selenium", "Selenium")]
|
[Trait("Selenium", "Selenium")]
|
||||||
public async Task CanUsePullPaymentsViaUI()
|
public async Task CanUsePullPaymentsViaUI()
|
||||||
{
|
{
|
||||||
using (var s = SeleniumTester.Create())
|
using var s = SeleniumTester.Create();
|
||||||
{
|
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
s.RegisterNewUser(true);
|
s.RegisterNewUser(true);
|
||||||
s.CreateNewStore();
|
s.CreateNewStore();
|
||||||
@@ -988,6 +987,8 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal("payout", s.Driver.FindElement(By.ClassName("transactionLabel")).Text);
|
Assert.Equal("payout", s.Driver.FindElement(By.ClassName("transactionLabel")).Text);
|
||||||
|
|
||||||
s.GoToWallet(navPages: WalletsNavPages.Payouts);
|
s.GoToWallet(navPages: WalletsNavPages.Payouts);
|
||||||
|
var x = s.Driver.PageSource;
|
||||||
|
s.Driver.FindElement(By.Id($"{PayoutState.InProgress}-view")).Click();
|
||||||
ReadOnlyCollection<IWebElement> txs;
|
ReadOnlyCollection<IWebElement> txs;
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
@@ -1000,14 +1001,14 @@ namespace BTCPayServer.Tests
|
|||||||
s.Driver.Navigate().GoToUrl(viewPullPaymentUrl);
|
s.Driver.Navigate().GoToUrl(viewPullPaymentUrl);
|
||||||
txs = s.Driver.FindElements(By.ClassName("transaction-link"));
|
txs = s.Driver.FindElements(By.ClassName("transaction-link"));
|
||||||
Assert.Equal(2, txs.Count);
|
Assert.Equal(2, txs.Count);
|
||||||
Assert.Contains("In Progress", s.Driver.PageSource);
|
Assert.Contains(PayoutState.InProgress.GetStateString(), s.Driver.PageSource);
|
||||||
|
|
||||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||||
|
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
s.Driver.Navigate().Refresh();
|
s.Driver.Navigate().Refresh();
|
||||||
Assert.Contains("Completed", s.Driver.PageSource);
|
Assert.Contains(PayoutState.Completed.GetStateString(), s.Driver.PageSource);
|
||||||
});
|
});
|
||||||
await s.Server.ExplorerNode.GenerateAsync(10);
|
await s.Server.ExplorerNode.GenerateAsync(10);
|
||||||
var pullPaymentId = viewPullPaymentUrl.Split('/').Last();
|
var pullPaymentId = viewPullPaymentUrl.Split('/').Last();
|
||||||
@@ -1019,7 +1020,6 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.True(payoutsData.All(p => p.State == PayoutState.Completed));
|
Assert.True(payoutsData.All(p => p.State == PayoutState.Completed));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static void CanBrowseContent(SeleniumTester s)
|
private static void CanBrowseContent(SeleniumTester s)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
public partial class WalletsController
|
public partial class WalletsController
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet("{walletId}/pull-payments/new")]
|
||||||
[Route("{walletId}/pull-payments/new")]
|
|
||||||
public IActionResult NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
|
public IActionResult NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
|
||||||
WalletId walletId)
|
WalletId walletId)
|
||||||
{
|
{
|
||||||
@@ -41,8 +40,7 @@ namespace BTCPayServer.Controllers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost("{walletId}/pull-payments/new")]
|
||||||
[Route("{walletId}/pull-payments/new")]
|
|
||||||
public async Task<IActionResult> NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
|
public async Task<IActionResult> NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
|
||||||
WalletId walletId, NewPullPaymentModel model)
|
WalletId walletId, NewPullPaymentModel model)
|
||||||
{
|
{
|
||||||
@@ -87,8 +85,7 @@ namespace BTCPayServer.Controllers
|
|||||||
return RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() });
|
return RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet("{walletId}/pull-payments")]
|
||||||
[Route("{walletId}/pull-payments")]
|
|
||||||
public async Task<IActionResult> PullPayments(
|
public async Task<IActionResult> PullPayments(
|
||||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||||
WalletId walletId)
|
WalletId walletId)
|
||||||
@@ -149,8 +146,7 @@ namespace BTCPayServer.Controllers
|
|||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet("{walletId}/pull-payments/{pullPaymentId}/archive")]
|
||||||
[Route("{walletId}/pull-payments/{pullPaymentId}/archive")]
|
|
||||||
public IActionResult ArchivePullPayment(
|
public IActionResult ArchivePullPayment(
|
||||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||||
WalletId walletId,
|
WalletId walletId,
|
||||||
@@ -164,8 +160,8 @@ namespace BTCPayServer.Controllers
|
|||||||
Action = "Archive"
|
Action = "Archive"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
[HttpPost]
|
|
||||||
[Route("{walletId}/pull-payments/{pullPaymentId}/archive")]
|
[HttpPost("{walletId}/pull-payments/{pullPaymentId}/archive")]
|
||||||
public async Task<IActionResult> ArchivePullPaymentPost(
|
public async Task<IActionResult> ArchivePullPaymentPost(
|
||||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||||
WalletId walletId,
|
WalletId walletId,
|
||||||
@@ -180,8 +176,7 @@ namespace BTCPayServer.Controllers
|
|||||||
return RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() });
|
return RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost("{walletId}/payouts")]
|
||||||
[Route("{walletId}/payouts")]
|
|
||||||
public async Task<IActionResult> PayoutsPost(
|
public async Task<IActionResult> PayoutsPost(
|
||||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||||
WalletId walletId, PayoutsModel vm, CancellationToken cancellationToken)
|
WalletId walletId, PayoutsModel vm, CancellationToken cancellationToken)
|
||||||
@@ -312,7 +307,7 @@ namespace BTCPayServer.Controllers
|
|||||||
});
|
});
|
||||||
if (result != PayoutPaidRequest.PayoutPaidResult.Ok)
|
if (result != PayoutPaidRequest.PayoutPaidResult.Ok)
|
||||||
{
|
{
|
||||||
this.TempData.SetStatusMessageModel(new StatusMessageModel()
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||||
{
|
{
|
||||||
Message = PayoutPaidRequest.GetErrorMessage(result),
|
Message = PayoutPaidRequest.GetErrorMessage(result),
|
||||||
Severity = StatusMessageModel.StatusSeverity.Error
|
Severity = StatusMessageModel.StatusSeverity.Error
|
||||||
@@ -362,65 +357,77 @@ namespace BTCPayServer.Controllers
|
|||||||
return payouts;
|
return payouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet("{walletId}/payouts")]
|
||||||
[Route("{walletId}/payouts")]
|
|
||||||
public async Task<IActionResult> Payouts(
|
public async Task<IActionResult> Payouts(
|
||||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||||
WalletId walletId, PayoutsModel vm = null)
|
WalletId walletId, string pullPaymentId, PayoutState payoutState,
|
||||||
|
int skip = 0, int count = 50)
|
||||||
{
|
{
|
||||||
vm ??= new PayoutsModel();
|
var vm = this.ParseListQuery(new PayoutsModel
|
||||||
vm.PayoutStateSets ??= ((PayoutState[]) Enum.GetValues(typeof(PayoutState))).Select(state =>
|
{
|
||||||
new PayoutsModel.PayoutStateSet() {State = state, Payouts = new List<PayoutsModel.PayoutModel>()}).ToList();
|
PaymentMethodId = new PaymentMethodId(walletId.CryptoCode, PaymentTypes.BTCLike),
|
||||||
using var ctx = this._dbContextFactory.CreateContext();
|
PullPaymentId = pullPaymentId,
|
||||||
|
PayoutState = payoutState,
|
||||||
|
Skip = skip,
|
||||||
|
Count = count
|
||||||
|
});
|
||||||
|
vm.Payouts = new List<PayoutsModel.PayoutModel>();
|
||||||
|
await using var ctx = _dbContextFactory.CreateContext();
|
||||||
var storeId = walletId.StoreId;
|
var storeId = walletId.StoreId;
|
||||||
vm.PaymentMethodId = new PaymentMethodId(walletId.CryptoCode, PaymentTypes.BTCLike);
|
|
||||||
var payoutRequest = ctx.Payouts.Where(p => p.PullPaymentData.StoreId == storeId && !p.PullPaymentData.Archived);
|
var payoutRequest = ctx.Payouts.Where(p => p.PullPaymentData.StoreId == storeId && !p.PullPaymentData.Archived);
|
||||||
if (vm.PullPaymentId != null)
|
if (vm.PullPaymentId != null)
|
||||||
{
|
{
|
||||||
payoutRequest = payoutRequest.Where(p => p.PullPaymentDataId == vm.PullPaymentId);
|
payoutRequest = payoutRequest.Where(p => p.PullPaymentDataId == vm.PullPaymentId);
|
||||||
|
vm.PullPaymentName = (await ctx.PullPayments.FindAsync(pullPaymentId)).GetBlob().Name;
|
||||||
}
|
}
|
||||||
|
if (vm.PaymentMethodId != null)
|
||||||
|
{
|
||||||
|
var pmiStr = vm.PaymentMethodId.ToString();
|
||||||
|
payoutRequest = payoutRequest.Where(p => p.PaymentMethodId == pmiStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.PayoutStateCount = payoutRequest.GroupBy(data => data.State)
|
||||||
|
.Select(e => new {e.Key, Count = e.Count()})
|
||||||
|
.ToDictionary(arg => arg.Key, arg => arg.Count);
|
||||||
|
foreach (PayoutState value in Enum.GetValues(typeof(PayoutState)))
|
||||||
|
{
|
||||||
|
if(vm.PayoutStateCount.ContainsKey(value))
|
||||||
|
continue;
|
||||||
|
vm.PayoutStateCount.Add(value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.PayoutStateCount = vm.PayoutStateCount.OrderBy(pair => pair.Key)
|
||||||
|
.ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
|
|
||||||
|
payoutRequest = payoutRequest.Where(p => p.State == vm.PayoutState);
|
||||||
|
vm.Total = await payoutRequest.CountAsync();
|
||||||
|
payoutRequest = payoutRequest.Skip(vm.Skip).Take(vm.Count);
|
||||||
|
|
||||||
var payouts = await payoutRequest.OrderByDescending(p => p.Date)
|
var payouts = await payoutRequest.OrderByDescending(p => p.Date)
|
||||||
.Select(o => new
|
.Select(o => new
|
||||||
{
|
{
|
||||||
Payout = o,
|
Payout = o,
|
||||||
PullPayment = o.PullPaymentData
|
PullPayment = o.PullPaymentData
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
foreach (var stateSet in payouts.GroupBy(arg => arg.Payout.State))
|
foreach (var item in payouts)
|
||||||
{
|
{
|
||||||
var state = vm.PayoutStateSets.SingleOrDefault(set => set.State == stateSet.Key);
|
|
||||||
if (state == null)
|
|
||||||
{
|
|
||||||
state = new PayoutsModel.PayoutStateSet()
|
|
||||||
{
|
|
||||||
Payouts = new List<PayoutsModel.PayoutModel>(), State = stateSet.Key
|
|
||||||
};
|
|
||||||
vm.PayoutStateSets.Add(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in stateSet)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (item.Payout.GetPaymentMethodId() != vm.PaymentMethodId)
|
|
||||||
continue;
|
|
||||||
var ppBlob = item.PullPayment.GetBlob();
|
var ppBlob = item.PullPayment.GetBlob();
|
||||||
var payoutBlob = item.Payout.GetBlob(_jsonSerializerSettings);
|
var payoutBlob = item.Payout.GetBlob(_jsonSerializerSettings);
|
||||||
var m = new PayoutsModel.PayoutModel();
|
var m = new PayoutsModel.PayoutModel
|
||||||
m.PullPaymentId = item.PullPayment.Id;
|
{
|
||||||
m.PullPaymentName = ppBlob.Name ?? item.PullPayment.Id;
|
PullPaymentId = item.PullPayment.Id,
|
||||||
m.Date = item.Payout.Date;
|
PullPaymentName = ppBlob.Name ?? item.PullPayment.Id,
|
||||||
m.PayoutId = item.Payout.Id;
|
Date = item.Payout.Date,
|
||||||
m.Amount = _currencyTable.DisplayFormatCurrency(payoutBlob.Amount, ppBlob.Currency);
|
PayoutId = item.Payout.Id,
|
||||||
m.Destination = payoutBlob.Destination;
|
Amount = _currencyTable.DisplayFormatCurrency(payoutBlob.Amount, ppBlob.Currency),
|
||||||
|
Destination = payoutBlob.Destination
|
||||||
|
};
|
||||||
var handler = _payoutHandlers
|
var handler = _payoutHandlers
|
||||||
.FirstOrDefault(handler => handler.CanHandle(item.Payout.GetPaymentMethodId()));
|
.FirstOrDefault(handler => handler.CanHandle(item.Payout.GetPaymentMethodId()));
|
||||||
var proofBlob = handler?.ParseProof(item.Payout);
|
var proofBlob = handler?.ParseProof(item.Payout);
|
||||||
m.ProofLink = proofBlob?.Link;
|
m.ProofLink = proofBlob?.Link;
|
||||||
state.Payouts.Add(m);
|
vm.Payouts.Add(m);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
vm.PayoutStateSets = vm.PayoutStateSets.Where(set => set.Payouts?.Any() is true).ToList();
|
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using BTCPayServer.Models;
|
|||||||
using BTCPayServer.Models.InvoicingModels;
|
using BTCPayServer.Models.InvoicingModels;
|
||||||
using BTCPayServer.Models.PaymentRequestViewModels;
|
using BTCPayServer.Models.PaymentRequestViewModels;
|
||||||
using BTCPayServer.Models.ServerViewModels;
|
using BTCPayServer.Models.ServerViewModels;
|
||||||
|
using BTCPayServer.Models.WalletViewModels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@@ -21,6 +22,8 @@ namespace BTCPayServer
|
|||||||
prop = typeof(UserPrefsCookie).GetProperty(nameof(UserPrefsCookie.PaymentRequestsQuery));
|
prop = typeof(UserPrefsCookie).GetProperty(nameof(UserPrefsCookie.PaymentRequestsQuery));
|
||||||
else if (model is UsersViewModel)
|
else if (model is UsersViewModel)
|
||||||
prop = typeof(UserPrefsCookie).GetProperty(nameof(UserPrefsCookie.UsersQuery));
|
prop = typeof(UserPrefsCookie).GetProperty(nameof(UserPrefsCookie.UsersQuery));
|
||||||
|
else if (model is PayoutsModel)
|
||||||
|
prop = typeof(UserPrefsCookie).GetProperty(nameof(UserPrefsCookie.UsersQuery));
|
||||||
else
|
else
|
||||||
throw new Exception("Unsupported BasePagingViewModel for cookie user preferences saving");
|
throw new Exception("Unsupported BasePagingViewModel for cookie user preferences saving");
|
||||||
|
|
||||||
@@ -78,6 +81,7 @@ namespace BTCPayServer
|
|||||||
|
|
||||||
public ListQueryDataHolder PaymentRequestsQuery { get; set; }
|
public ListQueryDataHolder PaymentRequestsQuery { get; set; }
|
||||||
public ListQueryDataHolder UsersQuery { get; set; }
|
public ListQueryDataHolder UsersQuery { get; set; }
|
||||||
|
public ListQueryDataHolder PayoutsQuery { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListQueryDataHolder
|
class ListQueryDataHolder
|
||||||
|
|||||||
@@ -2,18 +2,21 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.Data;
|
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
|
|
||||||
namespace BTCPayServer.Models.WalletViewModels
|
namespace BTCPayServer.Models.WalletViewModels
|
||||||
{
|
{
|
||||||
public class PayoutsModel
|
public class PayoutsModel : BasePagingViewModel
|
||||||
{
|
{
|
||||||
public string PullPaymentId { get; set; }
|
public string PullPaymentId { get; set; }
|
||||||
public string Command { get; set; }
|
public string Command { get; set; }
|
||||||
public List<PayoutStateSet> PayoutStateSets{ get; set; }
|
public Dictionary<PayoutState, int> PayoutStateCount { get; set; }
|
||||||
public PaymentMethodId PaymentMethodId { get; set; }
|
public PaymentMethodId PaymentMethodId { get; set; }
|
||||||
|
|
||||||
|
public List<PayoutModel> Payouts { get; set; }
|
||||||
|
public PayoutState PayoutState { get; set; }
|
||||||
|
public string PullPaymentName { get; set; }
|
||||||
|
|
||||||
public class PayoutModel
|
public class PayoutModel
|
||||||
{
|
{
|
||||||
public string PayoutId { get; set; }
|
public string PayoutId { get; set; }
|
||||||
@@ -26,16 +29,9 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
public string ProofLink { get; set; }
|
public string ProofLink { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PayoutStateSet
|
|
||||||
{
|
|
||||||
public PayoutState State { get; set; }
|
|
||||||
public List<PayoutModel> Payouts { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string[] GetSelectedPayouts(PayoutState state)
|
public string[] GetSelectedPayouts(PayoutState state)
|
||||||
{
|
{
|
||||||
return PayoutStateSets.Where(set => set.State == state)
|
return Payouts.Where(model => model.Selected).Select(model => model.PayoutId)
|
||||||
.SelectMany(set => set.Payouts.Where(model => model.Selected).Select(model => model.PayoutId))
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,22 @@
|
|||||||
@inject IEnumerable<IPayoutHandler> PayoutHandlers;
|
@inject IEnumerable<IPayoutHandler> PayoutHandlers;
|
||||||
@{
|
@{
|
||||||
Layout = "../Shared/_NavLayout.cshtml";
|
Layout = "../Shared/_NavLayout.cshtml";
|
||||||
ViewData.SetActivePageAndTitle(WalletsNavPages.Payouts, $"Manage {Model.PaymentMethodId.ToPrettyString()} payouts", Context.GetStoreData().StoreName);
|
ViewData.SetActivePageAndTitle(WalletsNavPages.Payouts, $"Manage {Model.PaymentMethodId.ToPrettyString()} payouts{(string.IsNullOrEmpty(Model.PullPaymentName) ? string.Empty : " for pull payment " + Model.PullPaymentName)}", Context.GetStoreData().StoreName);
|
||||||
|
|
||||||
|
var stateActions = new List<(string Action, string Text)>();
|
||||||
|
switch (Model.PayoutState)
|
||||||
|
{
|
||||||
|
case PayoutState.AwaitingApproval:
|
||||||
|
stateActions.Add(("approve", "Approve selected payouts"));
|
||||||
|
stateActions.Add(("approve-pay", "Approve & Send selected payouts"));
|
||||||
|
stateActions.Add(("cancel", "Cancel selected payouts"));
|
||||||
|
break;
|
||||||
|
case PayoutState.AwaitingPayment:
|
||||||
|
stateActions.Add(("pay", "Send selected payouts"));
|
||||||
|
stateActions.Add(("cancel", "Cancel selected payouts"));
|
||||||
|
stateActions.Add(("mark-paid", "Mark selected payouts as already paid"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section PageFootContent {
|
@section PageFootContent {
|
||||||
@@ -20,70 +35,47 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<h4 class="mb-3">@ViewData["Title"]</h4>
|
<input type="hidden" asp-for="PaymentMethodId"/>
|
||||||
@if (!Model.PayoutStateSets.Any())
|
<input type="hidden" asp-for="PayoutState"/>
|
||||||
{
|
<h4 class="mb-4">@ViewData["Title"]</h4>
|
||||||
<p class="text-secondary mt-3">
|
|
||||||
There are no payouts yet.
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
@for (var index = 0; index < Model.PayoutStateSets.Count; index++)
|
@foreach (var state in Model.PayoutStateCount)
|
||||||
{
|
{
|
||||||
var state = Model.PayoutStateSets[index];
|
|
||||||
<li class="nav-item py-0">
|
<li class="nav-item py-0">
|
||||||
<a class="nav-link me-1 @(index == 0 ? "active" : "")" data-bs-toggle="tab" href="#@state.State" role="tab">@state.State.GetStateString() (@state.Payouts.Count)</a>
|
<a id="@state.Key-view" asp-action="Payouts" asp-route-walletId="@Context.GetRouteValue("walletId")" asp-route-payoutState="@state.Key" asp-route-pullPaymentId="@Model.PullPaymentId" class="nav-link me-1 @(state.Key == Model.PayoutState ? "active" : "")" role="tab">@state.Key.GetStateString() (@state.Value)</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@if (Model.Payouts.Any() && stateActions.Any())
|
||||||
<div class="row">
|
|
||||||
<div class="tab-content w-100">
|
|
||||||
@for (var index = 0; index < Model.PayoutStateSets.Count; index++)
|
|
||||||
{
|
{
|
||||||
var state = Model.PayoutStateSets[index];
|
<div class="dropdown col-auto mt-4 ms-xl-auto mt-xl-0">
|
||||||
var stateActions = new List<(string Action, string Text)>();
|
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" id="@Model.PayoutState-actions">
|
||||||
switch (state.State)
|
|
||||||
{
|
|
||||||
case PayoutState.AwaitingApproval:
|
|
||||||
stateActions.Add(("approve", "Approve selected payouts"));
|
|
||||||
stateActions.Add(("approve-pay", "Approve & Send selected payouts"));
|
|
||||||
stateActions.Add(("cancel", "Cancel selected payouts"));
|
|
||||||
break;
|
|
||||||
case PayoutState.AwaitingPayment:
|
|
||||||
stateActions.Add(("pay", "Send selected payouts"));
|
|
||||||
stateActions.Add(("cancel", "Cancel selected payouts"));
|
|
||||||
stateActions.Add(("mark-paid", "Mark selected payouts as already paid"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
<div class="tab-pane @(index == 0 ? "active" : "") " id="@state.State" role="tabpanel">
|
|
||||||
<input type="hidden" asp-for="PayoutStateSets[index].State"/>
|
|
||||||
<input type="hidden" asp-for="PaymentMethodId"/>
|
|
||||||
|
|
||||||
@if (state.Payouts.Any() && stateActions.Any())
|
|
||||||
{
|
|
||||||
<div class="dropdown mt-2">
|
|
||||||
<button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" id="@state.State-actions">
|
|
||||||
Actions
|
Actions
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu" aria-labelledby="@state.State-actions">
|
<div class="dropdown-menu" aria-labelledby="@Model.PayoutState-actions">
|
||||||
@foreach (var action in stateActions)
|
@foreach (var action in stateActions)
|
||||||
{
|
{
|
||||||
<button type="submit" id="@state.State-@action.Action" name="Command" class="dropdown-item" role="button" value="@state.State-@action.Action">@action.Text</button>
|
<button type="submit" id="@Model.PayoutState-@action.Action" name="Command" class="dropdown-item" role="button" value="@Model.PayoutState-@action.Action">@action.Text</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@if (state.Payouts.Any())
|
@if (Model.Payouts.Any())
|
||||||
{
|
{
|
||||||
<table class="table table-sm table-responsive-lg">
|
<table class="table table-sm table-responsive-lg">
|
||||||
<thead class="thead-inverse">
|
<thead class="thead-inverse">
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<input id="@state.State-selectAllCheckbox" type="checkbox" class="form-check-input" onclick="selectAll(this, '@state.State.ToString()'); return true;"/>
|
<input id="@Model.PayoutState-selectAllCheckbox" type="checkbox" class="form-check-input" onclick="selectAll(this, '@Model.PayoutState.ToString()'); return true;"/>
|
||||||
</th>
|
</th>
|
||||||
<th style="min-width: 90px;" class="col-md-auto">
|
<th style="min-width: 90px;" class="col-md-auto">
|
||||||
Date
|
Date
|
||||||
@@ -91,21 +83,21 @@
|
|||||||
<th class="text-start">Source</th>
|
<th class="text-start">Source</th>
|
||||||
<th class="text-start">Destination</th>
|
<th class="text-start">Destination</th>
|
||||||
<th class="text-end">Amount</th>
|
<th class="text-end">Amount</th>
|
||||||
@if (state.State != PayoutState.AwaitingApproval)
|
@if (Model.PayoutState != PayoutState.AwaitingApproval)
|
||||||
{
|
{
|
||||||
<th class="text-end">Transaction</th>
|
<th class="text-end">Transaction</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@for (int i = 0; i < state.Payouts.Count; i++)
|
@for (int i = 0; i < Model.Payouts.Count; i++)
|
||||||
{
|
{
|
||||||
var pp = state.Payouts[i];
|
var pp = Model.Payouts[i];
|
||||||
<tr class="payout">
|
<tr class="payout">
|
||||||
<td>
|
<td>
|
||||||
<span>
|
<span>
|
||||||
<input type="checkbox" class="selection-item-@state.State.ToString() form-check-input" asp-for="PayoutStateSets[index].Payouts[i].Selected"/>
|
<input type="checkbox" class="selection-item-@Model.PayoutState.ToString() form-check-input" asp-for="Payouts[i].Selected"/>
|
||||||
<input type="hidden" asp-for="PayoutStateSets[index].Payouts[i].PayoutId"/>
|
<input type="hidden" asp-for="Payouts[i].PayoutId"/>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -120,7 +112,7 @@
|
|||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<span>@pp.Amount</span>
|
<span>@pp.Amount</span>
|
||||||
</td>
|
</td>
|
||||||
@if (state.State != PayoutState.AwaitingApproval)
|
@if (Model.PayoutState != PayoutState.AwaitingApproval)
|
||||||
{
|
{
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
@if (!(pp.ProofLink is null))
|
@if (!(pp.ProofLink is null))
|
||||||
@@ -136,11 +128,9 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p class="mb-0 p-4" id="@state.State-no-payouts">No payouts.</p>
|
<p class="mb-0 py-4" id="@Model.PayoutState-no-payouts">There are no payouts matching this criteria.</p>
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<vc:pager view-model="Model"/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user