You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
16 KiB
403 lines
16 KiB
{% extends "base.html" %}
|
|
|
|
{% block title %}Payment Plan #{{ plan.id }} - Plutus{% endblock %}
|
|
|
|
{% block content %}
|
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
|
<ul>
|
|
<li><a href="{{ url_for('main.index') }}">Dashboard</a></li>
|
|
<li><a href="{{ url_for('main.payment_plans_list') }}">Payment Plans</a></li>
|
|
<li class="is-active"><a href="#" aria-current="page">Plan #{{ plan.id }}</a></li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<div class="level">
|
|
<div class="level-left">
|
|
<div>
|
|
<h1 class="title">Payment Plan #{{ plan.id }}</h1>
|
|
<p class="subtitle">Created: {{ plan.Created.strftime('%Y-%m-%d %H:%M:%S') if plan.Created else 'Unknown' }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="level-right">
|
|
<div class="field is-grouped">
|
|
<div class="control">
|
|
<form method="POST" action="{{ url_for('main.payment_plans_toggle', plan_id=plan.id) }}" style="display: inline;">
|
|
<button class="button {% if plan.Enabled %}is-warning{% else %}is-success{% endif %}"
|
|
onclick="return confirm('Are you sure you want to {% if plan.Enabled %}disable{% else %}enable{% endif %} this payment plan?')">
|
|
<span class="icon">
|
|
<i class="fas {% if plan.Enabled %}fa-pause{% else %}fa-play{% endif %}"></i>
|
|
</span>
|
|
<span>{% if plan.Enabled %}Disable{% else %}Enable{% endif %}</span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<div class="control">
|
|
<a class="button is-info" href="{{ url_for('main.payment_plans_edit', plan_id=plan.id) }}">
|
|
<span class="icon"><i class="fas fa-edit"></i></span>
|
|
<span>Edit</span>
|
|
</a>
|
|
</div>
|
|
<div class="control">
|
|
<a class="button is-light" href="{{ url_for('main.payment_plans_list') }}">
|
|
<span class="icon"><i class="fas fa-arrow-left"></i></span>
|
|
<span>Back to List</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Payment Plan Status Banner -->
|
|
<div class="box">
|
|
<div class="level">
|
|
<div class="level-left">
|
|
<div class="level-item">
|
|
{% if plan.Enabled %}
|
|
<span class="icon is-large has-text-success">
|
|
<i class="fas fa-calendar-check fa-2x"></i>
|
|
</span>
|
|
{% else %}
|
|
<span class="icon is-large has-text-warning">
|
|
<i class="fas fa-calendar-times fa-2x"></i>
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="level-item">
|
|
<div>
|
|
{% if plan.Enabled %}
|
|
<h2 class="title is-4 has-text-success mb-2">Active Payment Plan</h2>
|
|
<p class="has-text-grey">This payment plan is currently active and processing payments.</p>
|
|
{% else %}
|
|
<h2 class="title is-4 has-text-warning mb-2">Inactive Payment Plan</h2>
|
|
<p class="has-text-grey">This payment plan is disabled and not processing payments.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="level-right">
|
|
<div class="level-item">
|
|
<div class="has-text-right">
|
|
<p class="title is-3 has-text-primary mb-2">{{ plan.Amount | currency }}</p>
|
|
<p class="has-text-grey">{{ plan.Frequency }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Customer Information -->
|
|
<div class="columns">
|
|
<div class="column is-half">
|
|
<div class="box">
|
|
<h3 class="title is-5">
|
|
<span class="icon"><i class="fas fa-user"></i></span>
|
|
Customer Information
|
|
</h3>
|
|
|
|
<div id="customerInfo" data-splynx-id="{{ plan.Splynx_ID }}">
|
|
<div class="has-text-centered py-4">
|
|
<span class="icon is-large">
|
|
<i class="fas fa-spinner fa-spin"></i>
|
|
</span>
|
|
<p>Loading customer details...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-half">
|
|
<div class="box">
|
|
<h3 class="title is-5">
|
|
<span class="icon"><i class="fas fa-cog"></i></span>
|
|
Plan Configuration
|
|
</h3>
|
|
|
|
<table class="table is-fullwidth">
|
|
<tbody>
|
|
<tr>
|
|
<td><strong>Plan ID</strong></td>
|
|
<td>#{{ plan.id }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Payment Amount</strong></td>
|
|
<td><strong class="has-text-success">{{ plan.Amount | currency }}</strong></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Frequency</strong></td>
|
|
<td>
|
|
<span class="tag {% if plan.Frequency == 'Weekly' %}is-warning{% elif plan.Frequency == 'Fortnightly' %}is-info{% else %}is-light{% endif %}">
|
|
{{ plan.Frequency }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Start Date</strong></td>
|
|
<td>
|
|
{{ plan.Start_Date.strftime('%Y-%m-%d') if plan.Start_Date else '-' }}
|
|
{% if plan.Start_Date %}
|
|
<br><small class="has-text-grey">Payments occur every {{ plan.Frequency.lower() }} from this date</small>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Payment Method</strong></td>
|
|
<td>
|
|
<code class="is-size-7">{{ plan.Stripe_Payment_Method[:20] }}{% if plan.Stripe_Payment_Method|length > 20 %}...{% endif %}</code>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Status</strong></td>
|
|
<td>
|
|
{% if plan.Enabled %}
|
|
<span class="tag is-success">Active</span>
|
|
{% else %}
|
|
<span class="tag is-danger">Inactive</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Created</strong></td>
|
|
<td>{{ plan.Created.strftime('%Y-%m-%d %H:%M:%S') if plan.Created else '-' }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Created By</strong></td>
|
|
<td>{{ plan.created_by or 'Unknown' }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Associated Payments -->
|
|
<div class="box">
|
|
<div class="level">
|
|
<div class="level-left">
|
|
<h3 class="title is-5">
|
|
<span class="icon"><i class="fas fa-list"></i></span>
|
|
Associated Payments
|
|
</h3>
|
|
</div>
|
|
<div class="level-right">
|
|
<div class="field">
|
|
<p class="control has-icons-left">
|
|
<input class="input" type="text" id="paymentsSearchInput" placeholder="Search payments...">
|
|
<span class="icon is-small is-left">
|
|
<i class="fas fa-search"></i>
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if associated_payments %}
|
|
<div class="table-container">
|
|
<table class="table is-fullwidth is-striped is-hoverable" id="paymentsTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Payment ID</th>
|
|
<th>Amount</th>
|
|
<th>Status</th>
|
|
<th>Payment Intent</th>
|
|
<th>Processed</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for payment in associated_payments %}
|
|
<tr data-payment-id="{{ payment.id }}"
|
|
data-amount="{{ payment.Payment_Amount }}"
|
|
data-status="{{ 'successful' if payment.Success == True else 'failed' if payment.Success == False else 'pending' }}">
|
|
<td>
|
|
<a href="{{ url_for('main.payment_detail', payment_id=payment.id) }}"
|
|
class="has-text-weight-semibold">
|
|
#{{ payment.id }}
|
|
</a>
|
|
</td>
|
|
<td>
|
|
<strong>{{ payment.Payment_Amount | currency }}</strong>
|
|
</td>
|
|
<td>
|
|
{% if payment.Success == True %}
|
|
<span class="tag is-success">Success</span>
|
|
{% elif payment.Success == False %}
|
|
<span class="tag is-danger">Failed</span>
|
|
{% else %}
|
|
<span class="tag is-warning">Pending</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if payment.Payment_Intent %}
|
|
<code class="is-size-7">{{ payment.Payment_Intent[:20] }}...</code>
|
|
{% else %}
|
|
-
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ payment.Created.strftime('%Y-%m-%d %H:%M') if payment.Created else '-' }}</td>
|
|
<td>
|
|
<a class="button is-small is-info"
|
|
href="{{ url_for('main.payment_detail', payment_id=payment.id) }}">
|
|
<span class="icon"><i class="fas fa-eye"></i></span>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Payments Summary -->
|
|
<div class="level mt-4">
|
|
<div class="level-left">
|
|
<div class="level-item">
|
|
<div>
|
|
<p class="title is-6">Payment Summary</p>
|
|
<p class="subtitle is-7">Total: {{ associated_payments|length }} payments</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="level-right">
|
|
<div class="level-item">
|
|
<div class="tags has-addons">
|
|
<span class="tag is-success">
|
|
{{ associated_payments|selectattr('Success', 'equalto', True)|list|length }} Successful
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="level-item">
|
|
<div class="tags has-addons">
|
|
<span class="tag is-danger">
|
|
{{ associated_payments|selectattr('Success', 'equalto', False)|list|length }} Failed
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="level-item">
|
|
<div class="tags has-addons">
|
|
<span class="tag is-warning">
|
|
{{ associated_payments|selectattr('Success', 'equalto', None)|list|length }} Pending
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="has-text-centered py-6">
|
|
<span class="icon is-large has-text-grey-light">
|
|
<i class="fas fa-receipt fa-3x"></i>
|
|
</span>
|
|
<p class="title is-5 has-text-grey">No Associated Payments</p>
|
|
<p class="subtitle is-6 has-text-grey">This payment plan hasn't processed any payments yet.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script>
|
|
// Load customer information
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const splynxId = {{ plan.Splynx_ID }};
|
|
const customerInfoDiv = document.getElementById('customerInfo');
|
|
|
|
fetch(`/api/splynx/${splynxId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data && data.id) {
|
|
displayCustomerInfo(data);
|
|
} else {
|
|
showCustomerError('Customer not found');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching customer:', error);
|
|
showCustomerError('Error loading customer details');
|
|
});
|
|
});
|
|
|
|
function displayCustomerInfo(customer) {
|
|
const customerInfoDiv = document.getElementById('customerInfo');
|
|
|
|
const infoHtml = `
|
|
<table class="table is-fullwidth">
|
|
<tbody>
|
|
<tr>
|
|
<td><strong>Customer ID</strong></td>
|
|
<td>
|
|
<a href="https://billing.interphone.com.au/admin/customers/view?id=${customer.id}"
|
|
target="_blank" class="tag is-info">${customer.id}</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Name</strong></td>
|
|
<td>${customer.name || 'N/A'}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Email</strong></td>
|
|
<td>${customer.email || 'N/A'}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Phone</strong></td>
|
|
<td>${customer.phone || 'N/A'}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Status</strong></td>
|
|
<td>
|
|
${customer.status === 'active'
|
|
? '<span class="tag is-success">Active</span>'
|
|
: `<span class="tag is-warning">${customer.status || 'Unknown'}</span>`
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Address</strong></td>
|
|
<td>
|
|
${customer.street_1 || ''} ${customer.street_2 || ''}<br>
|
|
${customer.city || ''} ${customer.zip_code || ''}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
`;
|
|
|
|
customerInfoDiv.innerHTML = infoHtml;
|
|
}
|
|
|
|
function showCustomerError(message) {
|
|
const customerInfoDiv = document.getElementById('customerInfo');
|
|
customerInfoDiv.innerHTML = `
|
|
<div class="has-text-centered py-4">
|
|
<span class="icon is-large has-text-danger">
|
|
<i class="fas fa-exclamation-triangle fa-2x"></i>
|
|
</span>
|
|
<p class="has-text-danger">${message}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Search functionality for associated payments
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const searchInput = document.getElementById('paymentsSearchInput');
|
|
const table = document.getElementById('paymentsTable');
|
|
|
|
if (!table || !searchInput) return; // No table to search
|
|
|
|
searchInput.addEventListener('input', function() {
|
|
const searchTerm = this.value.toLowerCase();
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
rows.forEach(function(row) {
|
|
const paymentId = row.dataset.paymentId;
|
|
const amount = row.dataset.amount;
|
|
const status = row.dataset.status;
|
|
const rowText = row.textContent.toLowerCase();
|
|
|
|
const matches = !searchTerm ||
|
|
paymentId.includes(searchTerm) ||
|
|
amount.includes(searchTerm) ||
|
|
status.includes(searchTerm) ||
|
|
rowText.includes(searchTerm);
|
|
|
|
row.style.display = matches ? '' : 'none';
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|