mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Improve payment print styles (#1977)
* Improve payment print styles Allows for export as invoice PDF to be used in accounting. Closes #1957. * Change Transaction ID wording * Minor payment request UI improvements * Add amount paid, rate and colorize payment status * Display rate at invoice level * Inherit text color in print * Show full date in print view * Rearrange payment details * Add received date for payments * Fix amount calculation * Fix validInvoice assignment
This commit is contained in:
@@ -234,9 +234,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var statusesAllowedToDisplay = new List<InvoiceStatus>() { InvoiceStatus.New };
|
var statusesAllowedToDisplay = new List<InvoiceStatus>() { InvoiceStatus.New };
|
||||||
var validInvoice = result.Invoices.FirstOrDefault(invoice =>
|
var validInvoice = result.Invoices.FirstOrDefault(invoice => statusesAllowedToDisplay.Contains(invoice.Status));
|
||||||
Enum.TryParse<InvoiceStatus>(invoice.Status, true, out var status) &&
|
|
||||||
statusesAllowedToDisplay.Contains(status));
|
|
||||||
|
|
||||||
if (validInvoice != null)
|
if (validInvoice != null)
|
||||||
{
|
{
|
||||||
@@ -297,7 +295,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invoices = result.Invoices.Where(requestInvoice =>
|
var invoices = result.Invoices.Where(requestInvoice =>
|
||||||
requestInvoice.Status.Equals(InvoiceState.ToString(InvoiceStatus.New),
|
requestInvoice.StatusFormatted.Equals(InvoiceState.ToString(InvoiceStatus.New),
|
||||||
StringComparison.InvariantCulture) && !requestInvoice.Payments.Any());
|
StringComparison.InvariantCulture) && !requestInvoice.Payments.Any());
|
||||||
|
|
||||||
if (!invoices.Any())
|
if (!invoices.Any())
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using PaymentRequestData = BTCPayServer.Data.PaymentRequestData;
|
using PaymentRequestData = BTCPayServer.Data.PaymentRequestData;
|
||||||
@@ -139,7 +141,9 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
public DateTime ExpiryDate { get; set; }
|
public DateTime ExpiryDate { get; set; }
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
public string AmountFormatted { get; set; }
|
public string AmountFormatted { get; set; }
|
||||||
public string Status { get; set; }
|
public InvoiceState State { get; set; }
|
||||||
|
public InvoiceStatus Status { get; set; }
|
||||||
|
public string StatusFormatted { get; set; }
|
||||||
|
|
||||||
public List<PaymentRequestInvoicePayment> Payments { get; set; }
|
public List<PaymentRequestInvoicePayment> Payments { get; set; }
|
||||||
public string Currency { get; set; }
|
public string Currency { get; set; }
|
||||||
@@ -149,6 +153,10 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
{
|
{
|
||||||
public string PaymentMethod { get; set; }
|
public string PaymentMethod { get; set; }
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
|
public string RateFormatted { get; set; }
|
||||||
|
public decimal Paid { get; set; }
|
||||||
|
public string PaidFormatted { get; set; }
|
||||||
|
public DateTime ReceivedDate { get; set; }
|
||||||
public string Link { get; set; }
|
public string Link { get; set; }
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,37 +98,52 @@ namespace BTCPayServer.PaymentRequest
|
|||||||
AnyPendingInvoice = pendingInvoice != null,
|
AnyPendingInvoice = pendingInvoice != null,
|
||||||
PendingInvoiceHasPayments = pendingInvoice != null &&
|
PendingInvoiceHasPayments = pendingInvoice != null &&
|
||||||
pendingInvoice.ExceptionStatus != InvoiceExceptionStatus.None,
|
pendingInvoice.ExceptionStatus != InvoiceExceptionStatus.None,
|
||||||
Invoices = invoices.Select(entity => new ViewPaymentRequestViewModel.PaymentRequestInvoice()
|
Invoices = invoices.Select(entity =>
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
var state = entity.GetInvoiceState();
|
||||||
Amount = entity.Price,
|
return new ViewPaymentRequestViewModel.PaymentRequestInvoice
|
||||||
AmountFormatted = _currencies.FormatCurrency(entity.Price, blob.Currency),
|
|
||||||
Currency = entity.Currency,
|
|
||||||
ExpiryDate = entity.ExpirationTime.DateTime,
|
|
||||||
Status = entity.GetInvoiceState().ToString(),
|
|
||||||
Payments = entity
|
|
||||||
.GetPayments()
|
|
||||||
.Select(paymentEntity =>
|
|
||||||
{
|
{
|
||||||
var paymentData = paymentEntity.GetCryptoPaymentData();
|
Id = entity.Id,
|
||||||
var paymentMethodId = paymentEntity.GetPaymentMethodId();
|
Amount = entity.Price,
|
||||||
if (paymentData is null || paymentMethodId is null)
|
AmountFormatted = _currencies.FormatCurrency(entity.Price, blob.Currency),
|
||||||
{
|
Currency = entity.Currency,
|
||||||
return null;
|
ExpiryDate = entity.ExpirationTime.DateTime,
|
||||||
}
|
State = state,
|
||||||
|
Status = state.Status,
|
||||||
|
StatusFormatted = state.ToString(),
|
||||||
|
Payments = entity
|
||||||
|
.GetPayments()
|
||||||
|
.Select(paymentEntity =>
|
||||||
|
{
|
||||||
|
var paymentData = paymentEntity.GetCryptoPaymentData();
|
||||||
|
var paymentMethodId = paymentEntity.GetPaymentMethodId();
|
||||||
|
if (paymentData is null || paymentMethodId is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
string txId = paymentData.GetPaymentId();
|
string txId = paymentData.GetPaymentId();
|
||||||
string link = GetTransactionLink(paymentMethodId, txId);
|
string link = GetTransactionLink(paymentMethodId, txId);
|
||||||
return new ViewPaymentRequestViewModel.PaymentRequestInvoicePayment()
|
var paymentMethod = entity.GetPaymentMethod(paymentMethodId);
|
||||||
{
|
var amount = paymentData.GetValue();
|
||||||
Amount = paymentData.GetValue(),
|
var rate = paymentMethod.Rate;
|
||||||
PaymentMethod = paymentMethodId.ToString(),
|
var paid = (amount - paymentEntity.NetworkFee) * rate;
|
||||||
Link = link,
|
|
||||||
Id = txId
|
return new ViewPaymentRequestViewModel.PaymentRequestInvoicePayment
|
||||||
};
|
{
|
||||||
})
|
Amount = amount,
|
||||||
.Where(payment => payment != null)
|
Paid = paid,
|
||||||
.ToList()
|
ReceivedDate = paymentEntity.ReceivedTime.DateTime,
|
||||||
|
PaidFormatted = _currencies.FormatCurrency(paid, blob.Currency),
|
||||||
|
RateFormatted = _currencies.FormatCurrency(rate, blob.Currency),
|
||||||
|
PaymentMethod = paymentMethodId.ToPrettyString(),
|
||||||
|
Link = link,
|
||||||
|
Id = txId
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.Where(payment => payment != null)
|
||||||
|
.ToList()
|
||||||
|
};
|
||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
@model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
|
@using BTCPayServer.Services.Invoices
|
||||||
|
@using BTCPayServer.Client.Models
|
||||||
|
@model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
|
||||||
|
|
||||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||||
@inject BTCPayServer.Services.BTCPayServerEnvironment env
|
@inject BTCPayServer.Services.BTCPayServerEnvironment env
|
||||||
@@ -6,6 +8,31 @@
|
|||||||
@{
|
@{
|
||||||
ViewData["Title"] = Model.Title;
|
ViewData["Title"] = Model.Title;
|
||||||
Layout = null;
|
Layout = null;
|
||||||
|
|
||||||
|
string StatusTextClass(InvoiceState state)
|
||||||
|
{
|
||||||
|
switch (state.Status)
|
||||||
|
{
|
||||||
|
case InvoiceStatus.Confirmed:
|
||||||
|
case InvoiceStatus.Complete:
|
||||||
|
case InvoiceStatus.Paid:
|
||||||
|
return "text-success";
|
||||||
|
case InvoiceStatus.Expired:
|
||||||
|
switch (state.ExceptionStatus)
|
||||||
|
{
|
||||||
|
case InvoiceExceptionStatus.PaidLate:
|
||||||
|
case InvoiceExceptionStatus.PaidPartial:
|
||||||
|
case InvoiceExceptionStatus.PaidOver:
|
||||||
|
return "text-warning";
|
||||||
|
default:
|
||||||
|
return "text-danger";
|
||||||
|
}
|
||||||
|
case InvoiceStatus.Invalid:
|
||||||
|
return "text-danger";
|
||||||
|
default:
|
||||||
|
return "text-warning";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -50,7 +77,8 @@
|
|||||||
<div class="col col-12 col-sm-6 col-lg-8 d-flex align-items-center">
|
<div class="col col-12 col-sm-6 col-lg-8 d-flex align-items-center">
|
||||||
<span class="text-muted text-nowrap">Last Updated</span>
|
<span class="text-muted text-nowrap">Last Updated</span>
|
||||||
|
|
||||||
<span class="text-nowrap" v-text="lastUpdated" v-cloak>@Model.LastUpdated.ToString("g")</span>
|
<span class="text-nowrap d-print-none" v-text="lastUpdated" v-cloak>@Model.LastUpdated.ToString("g")</span>
|
||||||
|
<span class="text-nowrap d-none d-print-block" v-text="lastUpdatedDate">@Model.LastUpdated.ToString("g")</span>
|
||||||
<button type="button" class="btn btn-link d-none d-lg-inline-block d-print-none border-0 p-0 ml-4 only-for-js" v-on:click="window.print" v-cloak>
|
<button type="button" class="btn btn-link d-none d-lg-inline-block d-print-none border-0 p-0 ml-4 only-for-js" v-on:click="window.print" v-cloak>
|
||||||
Print
|
Print
|
||||||
</button>
|
</button>
|
||||||
@@ -69,13 +97,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 pt-3 pb-2 col-md-4 py-md-0 col-lg-3 d-print-none">
|
<div class="col-12 pt-3 pb-2 col-md-4 py-md-0 col-lg-3">
|
||||||
<noscript>
|
<noscript>
|
||||||
@if (Model.IsPending && !Model.Archived)
|
@if (Model.IsPending && !Model.Archived)
|
||||||
{
|
{
|
||||||
@if (Model.AllowCustomPaymentAmounts && !Model.AnyPendingInvoice)
|
@if (Model.AllowCustomPaymentAmounts && !Model.AnyPendingInvoice)
|
||||||
{
|
{
|
||||||
<form method="get" asp-action="PayPaymentRequest" asp-route-id="@Model.Id">
|
<form method="get" asp-action="PayPaymentRequest" asp-route-id="@Model.Id" class="d-print-none">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-12 col-sm-6 col-md-12">
|
<div class="col col-12 col-sm-6 col-md-12">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@@ -93,12 +121,12 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a class="btn btn-primary d-inline-block w-100 text-nowrap @if (!(Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)) { @("btn-lg") }" asp-action="PayPaymentRequest" asp-route-id="@Model.Id">
|
<a class="btn btn-primary d-inline-block d-print-none w-100 text-nowrap @if (!(Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)) { @("btn-lg") }" asp-action="PayPaymentRequest" asp-route-id="@Model.Id">
|
||||||
Pay Invoice
|
Pay Invoice
|
||||||
</a>
|
</a>
|
||||||
if (Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)
|
if (Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)
|
||||||
{
|
{
|
||||||
<form method="get" asp-action="CancelUnpaidPendingInvoice" asp-route-id="@Model.Id" class="mt-2">
|
<form method="get" asp-action="CancelUnpaidPendingInvoice" asp-route-id="@Model.Id" class="mt-2 d-print-none">
|
||||||
<button class="btn btn-outline-secondary w-100 text-nowrap" type="submit">Cancel Invoice</button>
|
<button class="btn btn-outline-secondary w-100 text-nowrap" type="submit">Cancel Invoice</button>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
@@ -119,7 +147,7 @@
|
|||||||
</noscript>
|
</noscript>
|
||||||
<template v-if="srvModel.isPending && !srvModel.archived" class="d-print-none">
|
<template v-if="srvModel.isPending && !srvModel.archived" class="d-print-none">
|
||||||
<template v-if="srvModel.allowCustomPaymentAmounts && !srvModel.anyPendingInvoice">
|
<template v-if="srvModel.allowCustomPaymentAmounts && !srvModel.anyPendingInvoice">
|
||||||
<form v-on:submit="submitCustomAmountForm">
|
<form v-on:submit="submitCustomAmountForm" class="d-print-none">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-12 col-sm-6 col-md-12">
|
<div class="col col-12 col-sm-6 col-md-12">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@@ -141,13 +169,13 @@
|
|||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<button class="btn btn-primary w-100 d-flex align-items-center justify-content-center text-nowrap" :class="{ 'btn-lg': !(srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments)}" v-on:click="pay(null)" :disabled="loading">
|
<button class="btn btn-primary w-100 d-flex d-print-none align-items-center justify-content-center text-nowrap" :class="{ 'btn-lg': !(srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments)}" v-on:click="pay(null)" :disabled="loading">
|
||||||
<div v-if="loading" class="spinner-grow spinner-grow-sm mr-2" role="status">
|
<div v-if="loading" class="spinner-grow spinner-grow-sm mr-2" role="status">
|
||||||
<span class="sr-only">Loading...</span>
|
<span class="sr-only">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
<span>Pay Invoice</span>
|
<span>Pay Invoice</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-secondary mt-2 w-100 d-flex align-items-center justify-content-center text-nowrap" v-if="srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments" v-on:click="cancelPayment()" :disabled="loading">
|
<button class="btn btn-outline-secondary mt-2 w-100 d-flex d-print-none align-items-center justify-content-center text-nowrap" v-if="srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments" v-on:click="cancelPayment()" :disabled="loading">
|
||||||
<span v-if="loading" class="spinner-grow spinner-grow-sm mr-2" role="status">
|
<span v-if="loading" class="spinner-grow spinner-grow-sm mr-2" role="status">
|
||||||
<span class="sr-only">Loading...</span>
|
<span class="sr-only">Loading...</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -187,19 +215,19 @@
|
|||||||
<div class="jumbotron h-100 m-0 p-sm-5">
|
<div class="jumbotron h-100 m-0 p-sm-5">
|
||||||
<h2 class="h4 mb-3">Payment Details</h2>
|
<h2 class="h4 mb-3">Payment Details</h2>
|
||||||
<dl class="mb-0 mt-md-4">
|
<dl class="mb-0 mt-md-4">
|
||||||
<div class="d-flex flex-column mb-4">
|
<div class="d-flex d-print-inline-block flex-column mb-4 mr-5">
|
||||||
<dt class="h4 font-weight-normal text-nowrap text-primary order-2 order-sm-1 mb-0" v-text="srvModel.amountDueFormatted">@Model.AmountDueFormatted</dt>
|
<dt class="h4 font-weight-normal text-nowrap text-primary text-print-default order-2 order-sm-1 mb-0" v-text="srvModel.amountDueFormatted">@Model.AmountDueFormatted</dt>
|
||||||
<dd class="text-muted order-1 order-sm-2 mb-1">Amount due</dd>
|
<dd class="text-muted order-1 order-sm-2 mb-1">Amount due</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
|
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
|
||||||
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected/Model.Amount)*100)%" v-bind:style="{ width: (srvModel.amountCollected/srvModel.amount*100) + '%' }"></div>
|
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected/Model.Amount)*100)%" v-bind:style="{ width: (srvModel.amountCollected/srvModel.amount*100) + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column mb-4 d-sm-inline-flex mb-sm-0">
|
<div class="d-flex d-print-inline-block flex-column mb-4 mr-5 d-sm-inline-flex mb-sm-0">
|
||||||
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0" v-text="srvModel.amountCollectedFormatted">@Model.AmountCollectedFormatted</dt>
|
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0" v-text="srvModel.amountCollectedFormatted">@Model.AmountCollectedFormatted</dt>
|
||||||
<dd class="text-muted order-1 order-sm-2 mb-1">Amount paid</dd>
|
<dd class="text-muted order-1 order-sm-2 mb-1">Amount paid</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column mb-0 d-sm-inline-flex float-sm-right">
|
<div class="d-flex d-print-inline-block flex-column mb-0 d-sm-inline-flex float-sm-right">
|
||||||
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0" v-text="srvModel.amountFormatted">@Model.AmountFormatted</dt>
|
<dt class="h4 text-sm-right font-weight-normal text-nowrap order-2 order-sm-1 mb-0" v-text="srvModel.amountFormatted">@Model.AmountFormatted</dt>
|
||||||
<dd class="text-muted text-sm-right order-1 order-sm-2 mb-1">Total requested</dd>
|
<dd class="text-muted text-sm-right order-1 order-sm-2 mb-1">Total requested</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
@@ -222,8 +250,9 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="table-borderless">
|
<tr class="table-borderless">
|
||||||
<th class="font-weight-normal text-secondary" scope="col">Invoice Id</th>
|
<th class="font-weight-normal text-secondary" scope="col">Invoice Id</th>
|
||||||
<th class="font-weight-normal text-secondary">Price</th>
|
<th class="font-weight-normal text-secondary w-175px">Expiry</th>
|
||||||
<th class="font-weight-normal text-secondary">Expiry</th>
|
<th class="font-weight-normal text-secondary text-right w-100px">Amount</th>
|
||||||
|
<th class="font-weight-normal text-secondary text-right w-125px"></th>
|
||||||
<th class="font-weight-normal text-secondary text-right">Status</th>
|
<th class="font-weight-normal text-secondary text-right">Status</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -232,32 +261,37 @@
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@invoice.Id</td>
|
<td>@invoice.Id</td>
|
||||||
<td>@invoice.Amount @invoice.Currency</td>
|
|
||||||
<td>@invoice.ExpiryDate.ToString("g")</td>
|
<td>@invoice.ExpiryDate.ToString("g")</td>
|
||||||
<td class="text-right">@invoice.Status</td>
|
<td class="text-right">@invoice.AmountFormatted</td>
|
||||||
|
<td class="text-right"></td>
|
||||||
|
<td class="text-right text-print-default @StatusTextClass(invoice.State)">@invoice.StatusFormatted</td>
|
||||||
</tr>
|
</tr>
|
||||||
if (invoice.Payments != null && invoice.Payments.Any())
|
if (invoice.Payments != null && invoice.Payments.Any())
|
||||||
{
|
{
|
||||||
<tr class="table-borderless table-light">
|
<tr class="table-borderless table-light">
|
||||||
<th colspan="2" class="pl-3 font-weight-normal text-secondary">TX Id</th>
|
<th class="pl-3 font-weight-normal text-secondary">Transaction Id</th>
|
||||||
<th class="font-weight-normal text-secondary">Payment Method</th>
|
<th class="font-weight-normal text-secondary">Received</th>
|
||||||
<th class="font-weight-normal text-secondary text-right">Amount</th>
|
<th class="font-weight-normal text-secondary text-right">Paid</th>
|
||||||
|
<th class="font-weight-normal text-secondary text-right">Rate</th>
|
||||||
|
<th class="font-weight-normal text-secondary text-right">Payment</th>
|
||||||
</tr>
|
</tr>
|
||||||
@foreach (var payment in invoice.Payments)
|
@foreach (var payment in invoice.Payments)
|
||||||
{
|
{
|
||||||
<tr class="table-borderless table-light">
|
<tr class="table-borderless table-light">
|
||||||
<td colspan="2" class="pl-3 text-break">
|
<td class="pl-3 text-break">
|
||||||
@if (!string.IsNullOrEmpty(payment.Link))
|
@if (!string.IsNullOrEmpty(payment.Link))
|
||||||
{
|
{
|
||||||
<a href="@payment.Link" target="_blank">@payment.Id</a>
|
<a href="@payment.Link" class="text-print-default" target="_blank">@payment.Id</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span>@payment.Id</span>
|
<span>@payment.Id</span>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>@payment.PaymentMethod</td>
|
<td>@payment.ReceivedDate.ToString("g")</td>
|
||||||
<td class="text-right">@payment.Amount</td>
|
<td class="text-right">@payment.PaidFormatted</td>
|
||||||
|
<td class="text-right">@payment.RateFormatted</td>
|
||||||
|
<td class="text-right text-nowrap">@payment.Amount @payment.PaymentMethod</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,37 +304,45 @@
|
|||||||
<template v-if="!srvModel.invoices || srvModel.invoices.length == 0">
|
<template v-if="!srvModel.invoices || srvModel.invoices.length == 0">
|
||||||
<p class="text-muted">No payments made yet.</p>
|
<p class="text-muted">No payments made yet.</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else v-for="invoice of srvModel.invoices" :key="invoice.id">
|
<template v-else>
|
||||||
<table class="table my-0">
|
<table class="table my-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="table-borderless">
|
<tr class="table-borderless">
|
||||||
<th class="font-weight-normal text-secondary" scope="col">Invoice Id</th>
|
<th class="font-weight-normal text-secondary" scope="col">Invoice Id</th>
|
||||||
<th class="font-weight-normal text-secondary">Price</th>
|
<th class="font-weight-normal text-secondary w-175px">Expiry</th>
|
||||||
<th class="font-weight-normal text-secondary">Expiry</th>
|
<th class="font-weight-normal text-secondary text-right w-100px">Amount</th>
|
||||||
|
<th class="font-weight-normal text-secondary text-right w-125px"></th>
|
||||||
<th class="font-weight-normal text-secondary text-right">Status</th>
|
<th class="font-weight-normal text-secondary text-right">Status</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<template v-for="invoice of srvModel.invoices" :key="invoice.id">
|
||||||
<td>{{invoice.id}}</td>
|
<tr>
|
||||||
<td>{{invoice.amountFormatted}}</td>
|
<td>{{invoice.id}}</td>
|
||||||
<td>{{moment(invoice.expiryDate).format('L HH:mm')}}</td>
|
<td v-text="formatDate(invoice.expiryDate)"></td>
|
||||||
<td class="text-right">{{invoice.status}}</td>
|
<td class="text-right">{{invoice.amountFormatted}}</td>
|
||||||
</tr>
|
<td class="text-right"></td>
|
||||||
<template v-if="invoice.payments && invoice.payments.length > 0">
|
<td class="text-right text-print-default" :class="statusTextClass(invoice.statusFormatted)">{{invoice.statusFormatted}}</td>
|
||||||
<tr class="table-borderless table-light">
|
|
||||||
<th colspan="2" class="pl-3 font-weight-normal text-secondary">TX Id</th>
|
|
||||||
<th class="font-weight-normal text-secondary">Payment Method</th>
|
|
||||||
<th class="font-weight-normal text-secondary text-right">Amount</th>
|
|
||||||
</tr>
|
|
||||||
<tr v-for="payment of invoice.payments" class="table-borderless table-light">
|
|
||||||
<td colspan="2" class="pl-3 text-break">
|
|
||||||
<a v-if="payment.link" :href="payment.link" target="_blank">{{payment.id}}</a>
|
|
||||||
<span v-else>{{payment.id}}</span>
|
|
||||||
</td>
|
|
||||||
<td>{{formatPaymentMethod(payment.paymentMethod)}}</td>
|
|
||||||
<td class="text-right">{{payment.amount.noExponents()}}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
<template v-if="invoice.payments && invoice.payments.length > 0">
|
||||||
|
<tr class="table-borderless table-light">
|
||||||
|
<th class="pl-3 font-weight-normal text-secondary">Transaction Id</th>
|
||||||
|
<th class="font-weight-normal text-secondary">Received</th>
|
||||||
|
<th class="font-weight-normal text-secondary text-right">Paid</th>
|
||||||
|
<th class="font-weight-normal text-secondary text-right">Rate</th>
|
||||||
|
<th class="font-weight-normal text-secondary text-right">Payment</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="payment of invoice.payments" class="table-borderless table-light">
|
||||||
|
<td class="pl-3 text-break">
|
||||||
|
<a v-if="payment.link" :href="payment.link" class="text-print-default" target="_blank">{{payment.id}}</a>
|
||||||
|
<span v-else>{{payment.id}}</span>
|
||||||
|
</td>
|
||||||
|
<td v-text="formatDate(payment.receivedDate)"></td>
|
||||||
|
<td class="text-right">{{payment.paidFormatted}}</td>
|
||||||
|
<td class="text-right">{{payment.rateFormatted}}</td>
|
||||||
|
<td class="text-right text-nowrap">{{payment.amount.noExponents()}} {{payment.paymentMethod}}</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
<div class="h-100 d-flex flex-column">
|
<div class="h-100 d-flex flex-column">
|
||||||
@if (Model.IsPending)
|
@if (Model.IsPending)
|
||||||
{
|
{
|
||||||
<nav id="mainNav" class="navbar sticky-top py-3 py-lg-4 d-print-block">
|
<nav id="mainNav" class="navbar sticky-top py-3 py-lg-4 d-print-none">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form asp-action="ClaimPullPayment" asp-route-pullPaymentId="@Model.Id" class="w-100">
|
<form asp-action="ClaimPullPayment" asp-route-pullPaymentId="@Model.Id" class="w-100">
|
||||||
<div class="row align-items-center" style="width:calc(100% + 30px)">
|
<div class="row align-items-center" style="width:calc(100% + 30px)">
|
||||||
@@ -114,19 +114,19 @@
|
|||||||
<div class="jumbotron h-100 m-0 p-sm-5">
|
<div class="jumbotron h-100 m-0 p-sm-5">
|
||||||
<h2 class="h4 mb-3">Payment Details</h2>
|
<h2 class="h4 mb-3">Payment Details</h2>
|
||||||
<dl class="mb-0 mt-md-4">
|
<dl class="mb-0 mt-md-4">
|
||||||
<div class="d-flex flex-column mb-4">
|
<div class="d-flex d-print-inline-block flex-column mb-4 mr-5">
|
||||||
<dt class="h4 font-weight-normal text-nowrap text-primary order-2 order-sm-1 mb-0">@Model.AmountDueFormatted</dt>
|
<dt class="h4 font-weight-normal text-nowrap text-primary text-print-default order-2 order-sm-1 mb-0">@Model.AmountDueFormatted</dt>
|
||||||
<dd class="text-muted order-1 order-sm-2 mb-1">Available claim</dd>
|
<dd class="text-muted order-1 order-sm-2 mb-1">Available claim</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
|
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
|
||||||
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected / Model.Amount) * 100)%"></div>
|
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected / Model.Amount) * 100)%"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column mb-4 d-sm-inline-flex mb-sm-0">
|
<div class="d-flex d-print-inline-block flex-column mb-4 mr-5 d-sm-inline-flex mb-sm-0">
|
||||||
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0">@Model.AmountCollectedFormatted</dt>
|
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0">@Model.AmountCollectedFormatted</dt>
|
||||||
<dd class="text-muted order-1 order-sm-2 mb-1">Already claimed</dd>
|
<dd class="text-muted order-1 order-sm-2 mb-1">Already claimed</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column mb-0 d-sm-inline-flex float-sm-right">
|
<div class="d-flex d-print-inline-block flex-column mb-0 d-sm-inline-flex float-sm-right">
|
||||||
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0">@Model.AmountFormatted</dt>
|
<dt class="h4 text-sm-right font-weight-normal text-nowrap order-2 order-sm-1 mb-0">@Model.AmountFormatted</dt>
|
||||||
<dd class="text-muted text-sm-right order-1 order-sm-2 mb-1">Claim limit</dd>
|
<dd class="text-muted text-sm-right order-1 order-sm-2 mb-1">Claim limit</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
@@ -159,11 +159,11 @@
|
|||||||
<td class="text-right text-nowrap">
|
<td class="text-right text-nowrap">
|
||||||
@if (!string.IsNullOrEmpty(invoice.Link))
|
@if (!string.IsNullOrEmpty(invoice.Link))
|
||||||
{
|
{
|
||||||
<a class="transaction-link @StatusTextClass(invoice.Status)" href="@invoice.Link">@invoice.Status</a>
|
<a class="transaction-link text-print-default @StatusTextClass(invoice.Status)" href="@invoice.Link">@invoice.Status</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="@StatusTextClass(invoice.Status)">@invoice.Status</span>
|
<span class="text-print-default @StatusTextClass(invoice.Status)">@invoice.Status</span>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -249,6 +249,12 @@ pre {
|
|||||||
margin-top: .5rem;
|
margin-top: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-100px { width: 100px; }
|
||||||
|
.w-125px { width: 125px; }
|
||||||
|
.w-150px { width: 150px; }
|
||||||
|
.w-175px { width: 175px; }
|
||||||
|
.w-200px { width: 200px; }
|
||||||
|
|
||||||
/* Chrome, Safari, Edge, Opera */
|
/* Chrome, Safari, Edge, Opera */
|
||||||
input[type=number].hide-number-spin::-webkit-outer-spin-button,
|
input[type=number].hide-number-spin::-webkit-outer-spin-button,
|
||||||
input[type=number].hide-number-spin::-webkit-inner-spin-button {
|
input[type=number].hide-number-spin::-webkit-inner-spin-button {
|
||||||
@@ -268,8 +274,8 @@ html[data-devenv]:before {
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: var(--btcpay-bg-dark);
|
background: var(--btcpay-color-secondary-backdrop);
|
||||||
color: var(--btcpay-color-dark-text);
|
color: var(--btcpay-color-secondary-text);
|
||||||
opacity: .7;
|
opacity: .7;
|
||||||
padding: 4px 5px 3px 7px;
|
padding: 4px 5px 3px 7px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
@@ -285,9 +291,19 @@ html[data-devenv]:before {
|
|||||||
|
|
||||||
/* Print */
|
/* Print */
|
||||||
@media print {
|
@media print {
|
||||||
|
.table td,
|
||||||
|
.table th {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
.jumbotron {
|
.jumbotron {
|
||||||
padding: 1rem 0 !important;
|
padding: 1rem 0 !important;
|
||||||
}
|
}
|
||||||
|
.text-print-default {
|
||||||
|
color: inherit !important;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Richtext editor */
|
/* Richtext editor */
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ addLoadEvent(function (ev) {
|
|||||||
lastUpdated: function () {
|
lastUpdated: function () {
|
||||||
return this.srvModel.lastUpdated && moment(this.srvModel.lastUpdated).calendar();
|
return this.srvModel.lastUpdated && moment(this.srvModel.lastUpdated).calendar();
|
||||||
},
|
},
|
||||||
|
lastUpdatedDate: function () {
|
||||||
|
return this.srvModel.lastUpdated && moment(this.srvModel.lastUpdated).format('MMMM Do YYYY, h:mm:ss a');
|
||||||
|
},
|
||||||
active: function () {
|
active: function () {
|
||||||
return !this.ended;
|
return !this.ended;
|
||||||
}
|
}
|
||||||
@@ -90,26 +93,42 @@ addLoadEvent(function (ev) {
|
|||||||
self.timeoutState = setTimeout(function () {
|
self.timeoutState = setTimeout(function () {
|
||||||
self.setLoading(false);
|
self.setLoading(false);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
eventAggregator.$emit("cancel-invoice", amount);
|
eventAggregator.$emit("cancel-invoice", amount);
|
||||||
},
|
},
|
||||||
formatPaymentMethod: function (str) {
|
formatDate: function (date) {
|
||||||
|
return moment(date).format('L h:mm A')
|
||||||
if (str.endsWith("LightningLike")) {
|
|
||||||
return str.replace("LightningLike", "Lightning")
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
|
|
||||||
},
|
},
|
||||||
submitCustomAmountForm : function(e){
|
submitCustomAmountForm: function(e) {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
if(this.srvModel.allowCustomPaymentAmounts && parseFloat(this.customAmount) < this.srvModel.amountDue){
|
if (this.srvModel.allowCustomPaymentAmounts && parseFloat(this.customAmount) < this.srvModel.amountDue){
|
||||||
this.pay(parseFloat(this.customAmount));
|
this.pay(parseFloat(this.customAmount));
|
||||||
}else{
|
} else {
|
||||||
this.pay();
|
this.pay();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
statusTextClass: function (state) {
|
||||||
|
var [, status,, exceptionStatus] = state.match(/(\w*)\s?(\((\w*)\))?/) || [];
|
||||||
|
switch (status) {
|
||||||
|
case "confirmed":
|
||||||
|
case "complete":
|
||||||
|
case "paid":
|
||||||
|
return "text-success";
|
||||||
|
case "expired":
|
||||||
|
switch (exceptionStatus) {
|
||||||
|
case "paidLate":
|
||||||
|
case "paidPartial":
|
||||||
|
case "paidOver":
|
||||||
|
return "text-warning";
|
||||||
|
default:
|
||||||
|
return "text-danger";
|
||||||
|
}
|
||||||
|
case "invalid":
|
||||||
|
return "text-danger";
|
||||||
|
default:
|
||||||
|
return "text-warning";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user