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.
 
 
 

525 lines
19 KiB

{% extends "base.html" %}
{% block title %}Payment Plan #{{ plan.id }} - Plutus{% endblock %}
{% block head %}
<style>
/* Background styling */
body {
background-color: #3a3a3a !important;
background-image: url("{{ url_for('static', filename='images/plutus3.JPG') }}") !important;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
position: relative;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(58, 58, 58, 0.85);
z-index: -1;
}
/* Ensure content is visible */
.container-fluid, .container {
position: relative;
z-index: 1;
}
/* Ensure navbar and footer are above background */
.navbar, .footer, main {
position: relative;
z-index: 1;
}
/* Page title and breadcrumb styling */
h1, h2, h3, h4, h5, h6 {
color: #faf8f0 !important;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
}
.breadcrumb-item a {
color: #d4af37 !important;
}
.breadcrumb-item.active {
color: #faf8f0 !important;
}
/* Enhanced visibility */
.card, .info-card {
background-color: rgba(250, 248, 240, 0.98) !important;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}
.alert {
background-color: rgba(250, 248, 240, 0.98);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
/* Status banner styling with colorful borders */
.status-banner {
border-left: 5px solid;
border-radius: 0.375rem;
padding: 1.5rem;
}
.status-banner.active {
border-color: #28a745;
background: linear-gradient(135deg, #f0fff4 0%, #e8f5e9 100%);
}
.status-banner.inactive {
border-color: #ffc107;
background: linear-gradient(135deg, #fffbf0 0%, #fff3cd 100%);
}
.status-banner .status-icon {
font-size: 2.5rem;
}
.status-banner .status-title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.status-banner .status-text {
color: #6c757d;
margin-bottom: 0;
}
.amount-display {
font-size: 2rem;
font-weight: 700;
color: #d4af37;
margin-bottom: 0.25rem;
}
.frequency-text {
color: #6c757d;
font-size: 1rem;
}
/* Card styling with subtle shadows */
.info-card {
border-radius: 0.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: box-shadow 0.3s ease;
}
.info-card:hover {
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
}
.card-header-custom {
background: linear-gradient(135deg, #d4af37 0%, #c5a028 100%);
color: #000;
font-weight: 600;
padding: 1rem 1.25rem;
border-radius: 0.5rem 0.5rem 0 0;
}
/* Table styling enhancements */
.table-config td:first-child {
background-color: #f8f9fa;
font-weight: 600;
width: 40%;
}
/* Payment summary badges */
.summary-badge {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-weight: 600;
}
/* Loading spinner */
.spinner-container {
text-align: center;
padding: 2rem;
}
.spinner-border-custom {
width: 3rem;
height: 3rem;
color: #d4af37;
}
</style>
<script>
// Load customer information
document.addEventListener('DOMContentLoaded', function() {
console.log('Payment Plan Detail: DOM loaded, initializing customer fetch');
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 table-sm mb-0">
<tbody>
<tr>
<td style="width: 40%; background-color: #f8f9fa;"><strong>Customer ID</strong></td>
<td>
<a href="https://billing.interphone.com.au/admin/customers/view?id=${customer.id}"
target="_blank" class="badge bg-info text-decoration-none">${customer.id}</a>
</td>
</tr>
<tr>
<td style="background-color: #f8f9fa;"><strong>Name</strong></td>
<td>${customer.name || 'N/A'}</td>
</tr>
<tr>
<td style="background-color: #f8f9fa;"><strong>Email</strong></td>
<td>${customer.email || 'N/A'}</td>
</tr>
<tr>
<td style="background-color: #f8f9fa;"><strong>Phone</strong></td>
<td>${customer.phone || 'N/A'}</td>
</tr>
<tr>
<td style="background-color: #f8f9fa;"><strong>Status</strong></td>
<td>
${customer.status === 'active'
? '<span class="badge bg-success">Active</span>'
: `<span class="badge bg-warning">${customer.status || 'Unknown'}</span>`
}
</td>
</tr>
<tr>
<td style="background-color: #f8f9fa;"><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="text-center py-4">
<i class="fas fa-exclamation-triangle fa-3x text-danger mb-3"></i>
<p class="text-danger mb-0">${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 %}
{% block content %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('main.index') }}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('main.payment_plans_list') }}">Payment Plans</a></li>
<li class="breadcrumb-item active" aria-current="page">Plan #{{ plan.id }}</li>
</ol>
</nav>
<div class="d-flex justify-content-between align-items-start mb-4">
<div>
<h1 class="h2 mb-1">Payment Plan #{{ plan.id }}</h1>
<p class="text-muted">Created: {{ plan.Created.strftime('%Y-%m-%d %H:%M:%S') if plan.Created else 'Unknown' }}</p>
</div>
<div class="btn-group" role="group">
<form method="POST" action="{{ url_for('main.payment_plans_toggle', plan_id=plan.id) }}" style="display: inline;">
<button class="btn {% if plan.Enabled %}btn-warning{% else %}btn-success{% endif %}"
onclick="return confirm('Are you sure you want to {% if plan.Enabled %}disable{% else %}enable{% endif %} this payment plan?')">
<i class="fas {% if plan.Enabled %}fa-pause{% else %}fa-play{% endif %}"></i>
{% if plan.Enabled %}Disable{% else %}Enable{% endif %}
</button>
</form>
<a class="btn btn-info" href="{{ url_for('main.payment_plans_edit', plan_id=plan.id) }}">
<i class="fas fa-edit"></i> Edit
</a>
<a class="btn btn-light" href="{{ url_for('main.payment_plans_list') }}">
<i class="fas fa-arrow-left"></i> Back to List
</a>
</div>
</div>
<!-- Payment Plan Status Banner -->
<div class="status-banner {% if plan.Enabled %}active{% else %}inactive{% endif %} mb-4">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<div class="me-4">
{% if plan.Enabled %}
<i class="fas fa-calendar-check status-icon text-success"></i>
{% else %}
<i class="fas fa-calendar-times status-icon text-warning"></i>
{% endif %}
</div>
<div>
{% if plan.Enabled %}
<h2 class="status-title text-success">Active Payment Plan</h2>
<p class="status-text">This payment plan is currently active and processing payments.</p>
{% else %}
<h2 class="status-title text-warning">Inactive Payment Plan</h2>
<p class="status-text">This payment plan is disabled and not processing payments.</p>
{% endif %}
</div>
</div>
<div class="text-end">
<p class="amount-display">{{ plan.Amount | currency }}</p>
<p class="frequency-text">{{ plan.Frequency }}</p>
</div>
</div>
</div>
<!-- Customer Information -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card info-card">
<div class="card-header-custom">
<i class="fas fa-user me-2"></i>Customer Information
</div>
<div class="card-body">
<div id="customerInfo" data-splynx-id="{{ plan.Splynx_ID }}">
<div class="spinner-container">
<div class="spinner-border spinner-border-custom" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 text-muted">Loading customer details...</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card info-card">
<div class="card-header-custom">
<i class="fas fa-cog me-2"></i>Plan Configuration
</div>
<div class="card-body">
<table class="table table-sm table-config mb-0">
<tbody>
<tr>
<td>Plan ID</td>
<td>#{{ plan.id }}</td>
</tr>
<tr>
<td>Payment Amount</td>
<td><strong class="text-success">{{ plan.Amount | currency }}</strong></td>
</tr>
<tr>
<td>Frequency</td>
<td>
<span class="badge {% if plan.Frequency == 'Weekly' %}bg-warning{% elif plan.Frequency == 'Fortnightly' %}bg-info{% else %}bg-secondary{% endif %}">
{{ plan.Frequency }}
</span>
</td>
</tr>
<tr>
<td>Start Date</td>
<td>
{{ plan.Start_Date.strftime('%Y-%m-%d') if plan.Start_Date else '-' }}
{% if plan.Start_Date %}
<br><small class="text-muted">Payments occur every {{ plan.Frequency.lower() }} from this date</small>
{% endif %}
</td>
</tr>
<tr>
<td>Payment Method</td>
<td>
<code class="small">{{ plan.Stripe_Payment_Method[:20] }}{% if plan.Stripe_Payment_Method|length > 20 %}...{% endif %}</code>
</td>
</tr>
<tr>
<td>Status</td>
<td>
{% if plan.Enabled %}
<span class="badge bg-success">Active</span>
{% else %}
<span class="badge bg-danger">Inactive</span>
{% endif %}
</td>
</tr>
<tr>
<td>Created</td>
<td>{{ plan.Created.strftime('%Y-%m-%d %H:%M:%S') if plan.Created else '-' }}</td>
</tr>
<tr>
<td>Created By</td>
<td>{{ plan.created_by or 'Unknown' }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Associated Payments -->
<div class="card info-card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h3 class="h4 mb-0">
<i class="fas fa-list me-2"></i>Associated Payments
</h3>
<div class="input-group" style="max-width: 300px;">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input class="form-control" type="text" id="paymentsSearchInput" placeholder="Search payments...">
</div>
</div>
{% if associated_payments %}
<div class="table-responsive">
<table class="table table-striped table-hover" 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="fw-semibold text-primary">
#{{ payment.id }}
</a>
</td>
<td>
<strong>{{ payment.Payment_Amount | currency }}</strong>
</td>
<td>
{% if payment.Refund == True %}
<span class="status-badge refund">
<i class="fas fa-undo"></i>
Refund
</span>
{% elif payment.Success == True %}
<span class="status-badge success">
<i class="fas fa-check"></i>
Success
</span>
{% elif payment.Success == False and payment.PI_FollowUp %}
<span class="status-badge pending">
<i class="fas fa-clock"></i>
Pending
</span>
{% elif payment.Success == False %}
<span class="status-badge failed">
<i class="fas fa-times"></i>
Failed
</span>
{% else %}
<span class="status-badge pending">
<i class="fas fa-clock"></i>
Pending
</span>
{% endif %}
</td>
<td>
{% if payment.Payment_Intent %}
<code class="small">{{ 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="btn btn-sm btn-info"
href="{{ url_for('main.payment_detail', payment_id=payment.id) }}">
<i class="fas fa-eye"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Payments Summary -->
<div class="d-flex justify-content-between align-items-center mt-4 pt-3 border-top">
<div>
<p class="h6 mb-1">Payment Summary</p>
<p class="text-muted small mb-0">Total: {{ associated_payments|length }} payments</p>
</div>
<div class="d-flex gap-2">
<span class="badge bg-success summary-badge">
{{ associated_payments|selectattr('Success', 'equalto', True)|list|length }} Successful
</span>
<span class="badge bg-danger summary-badge">
{{ associated_payments|selectattr('Success', 'equalto', False)|list|length }} Failed
</span>
<span class="badge bg-warning summary-badge">
{{ associated_payments|selectattr('Success', 'equalto', None)|list|length }} Pending
</span>
</div>
</div>
{% else %}
<div class="text-center py-5">
<i class="fas fa-receipt fa-4x text-muted mb-3"></i>
<p class="h5 text-muted mb-2">No Associated Payments</p>
<p class="text-muted">This payment plan hasn't processed any payments yet.</p>
</div>
{% endif %}
</div>
</div>
{% endblock %}