mirror of
https://github.com/getnamingo/registry.git
synced 2025-06-29 07:33:27 +02:00
Added invoice view
This commit is contained in:
parent
5bdb5c2a65
commit
93f1a06a40
5 changed files with 184 additions and 1 deletions
|
@ -23,6 +23,39 @@ class FinancialsController extends Controller
|
|||
return view($response,'admin/financials/invoices.twig');
|
||||
}
|
||||
|
||||
public function viewInvoice(Request $request, Response $response, $args)
|
||||
{
|
||||
$invoiceNumberPattern = '/^[A-Za-z]+\d+-?\d+$/';
|
||||
|
||||
if (preg_match($invoiceNumberPattern, $args)) {
|
||||
$invoiceNumber = $args; // valid format
|
||||
} else {
|
||||
$this->container->get('flash')->addMessage('error', 'Invalid invoice number');
|
||||
return $response->withHeader('Location', '/invoices')->withStatus(302);
|
||||
}
|
||||
|
||||
$db = $this->container->get('db');
|
||||
$invoice_details = $db->selectRow('SELECT * FROM invoices WHERE invoice_number = ?',
|
||||
[ $invoiceNumber ]
|
||||
);
|
||||
$billing = $db->selectRow('SELECT * FROM registrar_contact WHERE id = ?',
|
||||
[ $invoice_details['billing_contact_id'] ]
|
||||
);
|
||||
$issueDate = new \DateTime($invoice_details['issue_date']);
|
||||
$firstDayPrevMonth = (clone $issueDate)->modify('first day of last month')->format('Y-m-d');
|
||||
$lastDayPrevMonth = (clone $issueDate)->modify('last day of last month')->format('Y-m-d');
|
||||
$statement = $db->select('SELECT * FROM statement WHERE date BETWEEN ? AND ? AND registrar_id = ?',
|
||||
[ $firstDayPrevMonth, $lastDayPrevMonth, $invoice_details['registrar_id'] ]
|
||||
);
|
||||
|
||||
return view($response,'admin/financials/viewInvoice.twig', [
|
||||
'invoice_details' => $invoice_details,
|
||||
'billing' => $billing,
|
||||
'statement' => $statement
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function deposit(Request $request, Response $response)
|
||||
{
|
||||
if ($_SESSION["auth_roles"] != 0) {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="col-12">
|
||||
{% include 'partials/flash.twig' %}
|
||||
<div class="card">
|
||||
<div class="card-body border-bottom py-3">
|
||||
<div class="d-flex">
|
||||
|
|
136
cp/resources/views/admin/financials/viewInvoice.twig
Normal file
136
cp/resources/views/admin/financials/viewInvoice.twig
Normal file
|
@ -0,0 +1,136 @@
|
|||
{% extends "layouts/app.twig" %}
|
||||
|
||||
{% block title %}{{ __('View Invoice') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-wrapper">
|
||||
<!-- Page header -->
|
||||
<div class="page-header d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="page-title">
|
||||
Invoice {{ invoice_details.invoice_number }}
|
||||
</h2>
|
||||
</div>
|
||||
<!-- Page title actions -->
|
||||
<div class="col-auto ms-auto d-print-none">
|
||||
<button type="button" class="btn btn-primary" onclick="javascript:window.print();">
|
||||
<!-- Download SVG icon from http://tabler-icons.io/i/printer -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 17h2a2 2 0 0 0 2 -2v-4a2 2 0 0 0 -2 -2h-14a2 2 0 0 0 -2 2v4a2 2 0 0 0 2 2h2" /><path d="M17 9v-4a2 2 0 0 0 -2 -2h-6a2 2 0 0 0 -2 2v4" /><path d="M7 13m0 2a2 2 0 0 1 2 -2h6a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-6a2 2 0 0 1 -2 -2z" /></svg>
|
||||
Print Invoice
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page body -->
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="card card-lg">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p class="h3">Provider / Registry</p>
|
||||
<address>
|
||||
Street Address<br>
|
||||
State, City<br>
|
||||
Region, Postal Code<br>
|
||||
ltd@example.com
|
||||
</address>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<p class="h3">Client / Registrar</p>
|
||||
<address>
|
||||
{{ billing.first_name }} {{ billing.last_name }}<br>
|
||||
{{ billing.org }}<br>
|
||||
{{ billing.street1 }}<br>
|
||||
{{ billing.city }}, {{ billing.sp }}<br>
|
||||
{{ billing.pc }}, {{ billing.cc }}<br>
|
||||
{{ billing.email }}
|
||||
</address>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p class="h3">Invoice Issued On: {{ invoice_details.issue_date|date("Y-m-d") }}</p>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<p class="h3">Due Date: {{ invoice_details.due_date|date("Y-m-d") }}</p>
|
||||
</div>
|
||||
<div class="col-12 my-5">
|
||||
<h1>Invoice {{ invoice_details.invoice_number }} {% set status = invoice_details.payment_status %}
|
||||
<span class="badge
|
||||
{% if status == 'unpaid' %}bg-red text-red-fg
|
||||
{% elseif status == 'paid' %}bg-green text-green-fg
|
||||
{% elseif status == 'overdue' %}bg-orange text-orange-fg
|
||||
{% elseif status == 'cancelled' %}bg-grey text-grey-fg
|
||||
{% else %}bg-secondary
|
||||
{% endif %}">
|
||||
{{ status|capitalize }}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-transparent table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center" style="width: 1%"></th>
|
||||
<th>Product</th>
|
||||
<th class="text-end" style="width: 1%">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% if statement is not empty %}
|
||||
{% set totalAmount = 0 %}
|
||||
{% for item in statement %}
|
||||
<tr>
|
||||
<td class="text-center">{{ loop.index }}</td>
|
||||
<td>
|
||||
<p class="strong mb-1">{{ item.command }} {{ item.domain_name }}</p>
|
||||
</td>
|
||||
<td class="text-end">{{ item.amount }}</td>
|
||||
</tr>
|
||||
{% set totalAmount = totalAmount + item.amount %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="2" class="text-center">No items found.</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td colspan="2" class="strong text-end">Subtotal</td>
|
||||
<td class="text-end">{{ totalAmount|number_format(2, '.', ',') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="strong text-end">Vat Rate</td>
|
||||
<td class="text-end">TODO</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="strong text-end">Vat Due</td>
|
||||
<td class="text-end">TODO</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="font-weight-bold text-uppercase text-end">Total Due</td>
|
||||
<td class="font-weight-bold text-end">{{ invoice_details.total_amount }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="text-secondary text-center mt-5">Notes: {{ invoice_details.notes }}</p>
|
||||
<p class="text-secondary text-center mt-5">Thank you very much for doing business with us. We look forward to working with
|
||||
you again!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer footer-transparent d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
|
||||
<ul class="list-inline list-inline-dots mb-0">
|
||||
<li class="list-inline-item">
|
||||
Copyright © 2023
|
||||
<a href="https://namingo.org" target="_blank" class="link-secondary">Namingo</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -7,6 +7,17 @@
|
|||
var table;
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
|
||||
function invoiceLinkFormatter(cell){
|
||||
var value = cell.getValue();
|
||||
return `<a href="/invoice/${cell.getRow().getData().invoice_number}" style="font-weight:bold;">${value}</a>`;
|
||||
}
|
||||
|
||||
function actionsFormatter(cell, formatterParams, onRendered) {
|
||||
return `
|
||||
<a class="btn btn-outline-info btn-icon" href="/invoice/${cell.getRow().getData().invoice_number}" title="View Invoice"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /><path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6" /></svg></a>
|
||||
`;
|
||||
}
|
||||
|
||||
table = new Tabulator("#invoicesTable", {
|
||||
ajaxURL:"/api/records/invoices?join=registrar", // Set the URL for your JSON data
|
||||
ajaxConfig:"GET",
|
||||
|
@ -24,10 +35,11 @@
|
|||
],
|
||||
columns:[
|
||||
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
||||
{title:"Number", field:"invoice_number", width:200, headerSort:true, responsive:0},
|
||||
{title:"Number", field:"invoice_number", width:200, headerSort:true, formatter: invoiceLinkFormatter, responsive:0},
|
||||
{title:"Registrar", field:"registrar_id.name", width:300, headerSort:true, responsive:0},
|
||||
{title:"Date", field:"issue_date", width:300, headerSort:true, responsive:0},
|
||||
{title:"Amount", field:"total_amount", width:200, headerSort:true, responsive:0},
|
||||
{title: "Actions", formatter: actionsFormatter, headerSort: false, download:false, hozAlign: "center", responsive:0, cellClick:function(e, cell){ e.stopPropagation(); }},
|
||||
],
|
||||
placeholder:function(){
|
||||
return this.getHeaderFilters().length ? "No Matching Data" : "No Data"; //set placeholder based on if there are currently any header filters
|
||||
|
|
|
@ -81,6 +81,7 @@ $app->group('', function ($route) {
|
|||
$route->get('/reports', ReportsController::class .':view')->setName('reports');
|
||||
|
||||
$route->get('/invoices', FinancialsController::class .':invoices')->setName('invoices');
|
||||
$route->get('/invoice/{invoice}', FinancialsController::class . ':viewInvoice')->setName('viewInvoice');
|
||||
$route->map(['GET', 'POST'], '/deposit', FinancialsController::class .':deposit')->setName('deposit');
|
||||
$route->map(['GET', 'POST'], '/create-payment', FinancialsController::class .':createPayment')->setName('createPayment');
|
||||
$route->map(['GET', 'POST'], '/payment-success', FinancialsController::class .':success')->setName('success');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue