
- Add Jinja2 templates and static files for web UI - Create frontend routes for invoice management - Implement home page with recent invoices list - Add invoice creation form with dynamic items - Create invoice search functionality - Implement invoice details view with status update - Add JavaScript for form validation and dynamic UI - Update main.py to serve static files - Update documentation
191 lines
5.4 KiB
HTML
191 lines
5.4 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Invoice {{ invoice.invoice_number }} - Invoice Generation Service{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="card">
|
|
<div class="invoice-header">
|
|
<h2>Invoice #{{ invoice.invoice_number }}</h2>
|
|
<span class="invoice-status status-{{ invoice.status.lower() }}">
|
|
{{ invoice.status }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="invoice-actions">
|
|
<button id="print-invoice" class="btn">Print Invoice</button>
|
|
|
|
<form method="POST" action="{{ url_for('update_status', invoice_id=invoice.id) }}" class="status-form">
|
|
<select name="status" id="invoice-status">
|
|
<option value="PENDING" {% if invoice.status == 'PENDING' %}selected{% endif %}>PENDING</option>
|
|
<option value="PAID" {% if invoice.status == 'PAID' %}selected{% endif %}>PAID</option>
|
|
<option value="CANCELLED" {% if invoice.status == 'CANCELLED' %}selected{% endif %}>CANCELLED</option>
|
|
</select>
|
|
<button type="submit" class="btn">Update Status</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="invoice-content">
|
|
<div class="invoice-section">
|
|
<h3>Invoice Details</h3>
|
|
<dl>
|
|
<dt>Invoice Number</dt>
|
|
<dd>{{ invoice.invoice_number }}</dd>
|
|
|
|
<dt>Date Created</dt>
|
|
<dd>{{ invoice.date_created.strftime('%Y-%m-%d') }}</dd>
|
|
|
|
<dt>Due Date</dt>
|
|
<dd>{{ invoice.due_date.strftime('%Y-%m-%d') }}</dd>
|
|
|
|
<dt>Status</dt>
|
|
<dd>
|
|
<span class="invoice-status status-{{ invoice.status.lower() }}">
|
|
{{ invoice.status }}
|
|
</span>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
|
|
<div class="invoice-section">
|
|
<h3>Customer Information</h3>
|
|
<dl>
|
|
<dt>Name</dt>
|
|
<dd>{{ invoice.customer_name }}</dd>
|
|
|
|
{% if invoice.customer_email %}
|
|
<dt>Email</dt>
|
|
<dd>{{ invoice.customer_email }}</dd>
|
|
{% endif %}
|
|
|
|
{% if invoice.customer_address %}
|
|
<dt>Address</dt>
|
|
<dd>{{ invoice.customer_address }}</dd>
|
|
{% endif %}
|
|
</dl>
|
|
</div>
|
|
|
|
{% if invoice.notes %}
|
|
<div class="invoice-section">
|
|
<h3>Notes</h3>
|
|
<p>{{ invoice.notes }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="invoice-section">
|
|
<h3>Invoice Items</h3>
|
|
<table class="items-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Description</th>
|
|
<th>Quantity</th>
|
|
<th>Unit Price</th>
|
|
<th>Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in invoice.items %}
|
|
<tr>
|
|
<td>{{ item.description }}</td>
|
|
<td>{{ item.quantity }}</td>
|
|
<td>${{ "%.2f"|format(item.unit_price) }}</td>
|
|
<td>${{ "%.2f"|format(item.amount) }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="3" class="total-label">Total Amount</td>
|
|
<td class="total-amount">${{ "%.2f"|format(invoice.total_amount) }}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.invoice-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.invoice-actions {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 2rem;
|
|
padding-bottom: 1rem;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.status-form {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.status-form select {
|
|
padding: 0.5rem;
|
|
border-radius: 4px;
|
|
border: 1px solid #ddd;
|
|
}
|
|
|
|
.invoice-section {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.invoice-section h3 {
|
|
color: #3498db;
|
|
margin-bottom: 1rem;
|
|
padding-bottom: 0.5rem;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.invoice-section dl {
|
|
display: grid;
|
|
grid-template-columns: 1fr 3fr;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.invoice-section dt {
|
|
font-weight: 600;
|
|
color: #7f8c8d;
|
|
}
|
|
|
|
.items-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.items-table th,
|
|
.items-table td {
|
|
padding: 0.75rem;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
|
|
.items-table th {
|
|
background-color: #f5f7fa;
|
|
}
|
|
|
|
.total-label {
|
|
text-align: right;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.total-amount {
|
|
font-weight: 700;
|
|
font-size: 1.1rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.getElementById('print-invoice').addEventListener('click', function() {
|
|
window.print();
|
|
});
|
|
</script>
|
|
{% endblock %} |