mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-23 22:46:49 +01:00
* Add ability to set invoice status from details page * Remove unnecessary "using" statements * Add print styles * Fix Safari issues * Simplify JS * Update status badge class names * Update dropdown toggle padding * Adjust dropdown menu padding
384 lines
17 KiB
Text
384 lines
17 KiB
Text
@model InvoiceDetailsModel
|
|
@{
|
|
ViewData["Title"] = $"Invoice {Model.Id}";
|
|
}
|
|
|
|
@section PageHeadContent {
|
|
<meta name="robots" content="noindex,nofollow">
|
|
<style>
|
|
#posData td > table:last-child { margin-bottom: 0 !important; }
|
|
#posData table > tbody > tr:first-child > td > h4 { margin-top: 0 !important; }
|
|
</style>
|
|
}
|
|
|
|
@section PageFootContent {
|
|
<script>
|
|
function changeInvoiceState(invoiceId, newState) {
|
|
var toggleButton = $("#markStatusDropdownMenuButton");
|
|
toggleButton.attr("disabled", "disabled");
|
|
|
|
$.post(invoiceId + "/changestate/" + newState)
|
|
.done(function (data) {
|
|
var alertClassModifier = {
|
|
"complete (marked)": 'success',
|
|
"invalid (marked)": 'danger'
|
|
}[data.statusString];
|
|
var statusHtml = "<span class='fs-6 fw-normal badge bg-" + alertClassModifier + "'>" + data.statusString + " <span class='fa fa-check'></span></span>"
|
|
toggleButton.replaceWith(statusHtml);
|
|
})
|
|
.fail(function () {
|
|
toggleButton.removeAttr("disabled");
|
|
alert("Invoice state update failed");
|
|
});
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
$("[data-change-invoice-status-button]").click(function (e) {
|
|
var id = e.currentTarget.getAttribute('data-id');
|
|
var status = e.currentTarget.getAttribute('data-status');
|
|
|
|
changeInvoiceState(id, status);
|
|
});
|
|
});
|
|
</script>
|
|
}
|
|
|
|
<section class="invoice-details">
|
|
<div class="container">
|
|
<partial name="_StatusMessage" />
|
|
|
|
<div class="row mb-4">
|
|
<h2 class="col-xs-12 col-lg-6 mb-4 mb-lg-0">@ViewData["Title"]</h2>
|
|
<div class="col-xs-12 col-lg-6 mb-2 mb-lg-0 text-lg-end">
|
|
<div class="d-inline-flex">
|
|
@if (Model.ShowCheckout)
|
|
{
|
|
<a asp-action="Checkout" class="invoice-checkout-link btn btn-primary text-nowrap ms-2" asp-route-invoiceId="@Model.Id">
|
|
<i class="fa fa-qrcode"></i>
|
|
Checkout
|
|
</a>
|
|
}
|
|
|
|
@if (Model.CanRefund)
|
|
{
|
|
<a id="refundlink" class="btn btn-success text-nowrap ms-2" asp-action="Refund" asp-route-invoiceId="@Context.GetRouteValue("invoiceId")"><span class="fa fa-undo"></span> Issue refund</a>
|
|
}
|
|
else
|
|
{
|
|
<button href="#" class="btn btn-secondary text-nowrap ms-2" data-bs-toggle="tooltip" title="You can only issue refunds on invoices with confirmed payments" disabled><span class="fa fa-undo"></span> Issue refund</button>
|
|
}
|
|
<form class="p-0 ms-2" asp-action="ToggleArchive" asp-route-invoiceId="@Model.Id" method="post">
|
|
<button type="submit" class="btn @(Model.Archived ? "btn-warning" : "btn btn-danger")" id="btn-archive-toggle">
|
|
@if (Model.Archived)
|
|
{
|
|
<span class="text-nowrap" data-bs-toggle="tooltip" title="Unarchive this invoice"><i class="ms-1 fa fa-close"></i> Archived</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="text-nowrap" data-bs-toggle="tooltip" title="Archive this invoice so that it does not appear in the invoice list by default"><i class="ms-1 fa fa-archive"></i> Archive</span>
|
|
}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<h3 class="mb-3">Invoice Information</h3>
|
|
<table class="table table-responsive-md">
|
|
<tr>
|
|
<th>Store</th>
|
|
<td><a href="@Model.StoreLink" rel="noreferrer noopener">@Model.StoreName</a></td>
|
|
</tr>
|
|
<tr>
|
|
<th>Invoice Id</th>
|
|
<td>@Model.Id</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Order Id</th>
|
|
<td>
|
|
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderUrl))
|
|
{
|
|
@Model.TypedMetadata.OrderId
|
|
}
|
|
else
|
|
{
|
|
<a href="@Model.TypedMetadata.OrderUrl" rel="noreferrer noopener" target="_blank">
|
|
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderId))
|
|
{
|
|
<span>View Order</span>
|
|
}
|
|
else
|
|
{
|
|
@Model.TypedMetadata.OrderId
|
|
}
|
|
</a>
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Payment Request Id</th>
|
|
<td><a href="@Model.PaymentRequestLink" rel="noreferrer noopener">@Model.TypedMetadata.PaymentRequestId</a></td>
|
|
</tr>
|
|
<tr>
|
|
<th>State</th>
|
|
<td>
|
|
@if (Model.CanMarkStatus)
|
|
{
|
|
<div class="dropdown">
|
|
<button class="btn btn-secondary btn-sm dropdown-toggle py-1 px-2" type="button" id="markStatusDropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
@Model.State
|
|
</button>
|
|
<div class="dropdown-menu" aria-labelledby="markStatusDropdownMenuButton">
|
|
@if (Model.CanMarkInvalid)
|
|
{
|
|
<a class="dropdown-item" href="#" data-id="@Model.Id" data-status="invalid" data-change-invoice-status-button>
|
|
Mark as invalid <span class="fa fa-times"></span>
|
|
</a>
|
|
}
|
|
@if (Model.CanMarkComplete)
|
|
{
|
|
<a class="dropdown-item" href="#" data-id="@Model.Id" data-status="complete" data-change-invoice-status-button>
|
|
Mark as complete <span class="fa fa-check-circle"></span>
|
|
</a>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
else {
|
|
@Model.State
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Created date</th>
|
|
<td>@Model.CreatedDate.ToBrowserDate()</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Expiration date</th>
|
|
<td>@Model.ExpirationDate.ToBrowserDate()</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Monitoring date</th>
|
|
<td>@Model.MonitoringDate.ToBrowserDate()</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Transaction speed</th>
|
|
<td>@Model.TransactionSpeed</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Total fiat due</th>
|
|
<td>@Model.Fiat</td>
|
|
</tr>
|
|
@if (!string.IsNullOrEmpty(Model.RefundEmail))
|
|
{
|
|
<tr>
|
|
<th>Refund email</th>
|
|
<td><a href="mailto:@Model.RefundEmail">@Model.RefundEmail</a></td>
|
|
</tr>
|
|
}
|
|
@if (!string.IsNullOrEmpty(Model.NotificationUrl))
|
|
{
|
|
<tr>
|
|
<th>Notification Url</th>
|
|
<td>@Model.NotificationUrl</td>
|
|
</tr>
|
|
}
|
|
@if (!string.IsNullOrEmpty(Model.RedirectUrl))
|
|
{
|
|
<tr>
|
|
<th>Redirect Url</th>
|
|
<td><a href="@Model.RedirectUrl" rel="noreferrer noopener">@Model.RedirectUrl</a></td>
|
|
</tr>
|
|
}
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<h3 class="mb-3">Buyer Information</h3>
|
|
<table class="table table-responsive-md">
|
|
<tr>
|
|
<th>Name</th>
|
|
<td>@Model.TypedMetadata.BuyerName</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Email</th>
|
|
<td><a href="mailto:@Model.TypedMetadata.BuyerEmail">@Model.TypedMetadata.BuyerEmail</a></td>
|
|
</tr>
|
|
<tr>
|
|
<th>Phone</th>
|
|
<td>@Model.TypedMetadata.BuyerPhone</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Address 1</th>
|
|
<td>@Model.TypedMetadata.BuyerAddress1</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Address 2</th>
|
|
<td>@Model.TypedMetadata.BuyerAddress2</td>
|
|
</tr>
|
|
<tr>
|
|
<th>City</th>
|
|
<td>@Model.TypedMetadata.BuyerCity</td>
|
|
</tr>
|
|
<tr>
|
|
<th>State</th>
|
|
<td>@Model.TypedMetadata.BuyerState</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Country</th>
|
|
<td>@Model.TypedMetadata.BuyerCountry</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Zip</th>
|
|
<td>@Model.TypedMetadata.BuyerZip</td>
|
|
</tr>
|
|
</table>
|
|
@if (Model.PosData.Count == 0)
|
|
{
|
|
<h3 class="mb-3">Product Information</h3>
|
|
<table class="table table-responsive-md">
|
|
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
|
{
|
|
<tr>
|
|
<th>Item code</th>
|
|
<td>@Model.TypedMetadata.ItemCode</td>
|
|
</tr>
|
|
}
|
|
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
|
{
|
|
<tr>
|
|
<th>Item Description</th>
|
|
<td>@Model.TypedMetadata.ItemDesc</td>
|
|
</tr>
|
|
}
|
|
<tr>
|
|
<th>Price</th>
|
|
<td>@Model.Fiat</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Tax included</th>
|
|
<td>@Model.TaxIncluded</td>
|
|
</tr>
|
|
</table>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
@if (Model.PosData.Count != 0)
|
|
{
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<h3 class="mb-3">Product information</h3>
|
|
<table class="table table-responsive-md">
|
|
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
|
{
|
|
<tr>
|
|
<th>Item code</th>
|
|
<td>@Model.TypedMetadata.ItemCode</td>
|
|
</tr>
|
|
}
|
|
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
|
{
|
|
<tr>
|
|
<th>Item Description</th>
|
|
<td>@Model.TypedMetadata.ItemDesc</td>
|
|
</tr>
|
|
}
|
|
<tr>
|
|
<th>Price</th>
|
|
<td>@Model.Fiat</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Tax included</th>
|
|
<td>@Model.TaxIncluded</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6 mb-4" id="posData">
|
|
<h3 class="mb-3">Point of Sale Data</h3>
|
|
|
|
<partial name="PosData" model="(Model.PosData, 1)" />
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<partial name="ListInvoicesPaymentsPartial" model="(Model, true)" />
|
|
|
|
@if (Model.Deliveries.Count != 0)
|
|
{
|
|
<h3 class="mb-3">Webhook deliveries</h3>
|
|
<ul class="list-group mb-5">
|
|
@foreach (var delivery in Model.Deliveries)
|
|
{
|
|
<li class="list-group-item ">
|
|
<form
|
|
asp-action="RedeliverWebhook"
|
|
asp-route-storeId="@Model.StoreId"
|
|
asp-route-invoiceId="@Model.Id"
|
|
asp-route-deliveryId="@delivery.Id"
|
|
method="post">
|
|
<div class="d-flex align-items-center">
|
|
<span class="d-flex align-items-center flex-fill me-3">
|
|
@if (delivery.Success)
|
|
{
|
|
<span class="d-flex align-items-center fa fa-check text-success" title="Success"></span>
|
|
}
|
|
else
|
|
{
|
|
<span class="d-flex align-items-center fa fa-times text-danger" title="@delivery.ErrorMessage"></span>
|
|
}
|
|
<span class="ms-3">
|
|
<a
|
|
asp-action="WebhookDelivery"
|
|
asp-route-invoiceId="@Model.Id"
|
|
asp-route-deliveryId="@delivery.Id"
|
|
class="btn btn-link delivery-content" target="_blank">
|
|
@delivery.Id
|
|
</a>
|
|
<span class="text-light mx-2">|</span>
|
|
<span class="small text-muted">@delivery.Type</span>
|
|
</span>
|
|
</span>
|
|
<span class="d-flex align-items-center">
|
|
<strong class="d-flex align-items-center text-muted small">
|
|
@delivery.Time.ToBrowserDate()
|
|
</strong>
|
|
|
|
<button id="#redeliver-@delivery.Id"
|
|
type="submit"
|
|
class="btn btn-info py-1 ms-3 redeliver">
|
|
Redeliver
|
|
</button>
|
|
</span>
|
|
</div>
|
|
</form>
|
|
</li>
|
|
}
|
|
</ul>
|
|
}
|
|
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<h3 class="mb-0">Events</h3>
|
|
<table class="table table-hover table-responsive-md">
|
|
<thead class="thead-inverse">
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Message</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var evt in Model.Events)
|
|
{
|
|
<tr class="text-@evt.GetCssClass()">
|
|
<td>@evt.Timestamp.ToBrowserDate()</td>
|
|
<td>@evt.Message</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|