feat(reports): add Envelope, Phone Book (address+phone) and Rolodex Info reports

- PDF builders in app/reporting.py (envelope, phone+address, rolodex info)
- Endpoints in app/main.py with auth, filtering, logging, Content-Disposition
- New HTML template report_phone_book_address.html
- Rolodex bulk actions updated with buttons/links
- JS helper to submit selections to alternate endpoints

Tested via docker compose build/up and health check.
This commit is contained in:
HotSwapp
2025-10-07 17:50:03 -05:00
parent 684b947651
commit aeb0be6982
7 changed files with 508 additions and 1 deletions

View File

@@ -0,0 +1,74 @@
{% extends "base.html" %}
{% block title %}Phone Book (Address + Phone) · Delphi Database{% endblock %}
{% block content %}
<div class="row g-3">
<div class="col-12 d-flex align-items-center">
<a class="btn btn-sm btn-outline-secondary me-2" href="/rolodex">
<i class="bi bi-arrow-left"></i> Back
</a>
<h2 class="mb-0">Phone Book (Address + Phone)</h2>
<div class="ms-auto d-flex gap-2">
<a class="btn btn-outline-secondary btn-sm" href="/reports/phone-book-address?format=csv{% for id in client_ids %}&client_ids={{ id }}{% endfor %}{% if q %}&q={{ q | urlencode }}{% endif %}{% if phone %}&phone={{ phone | urlencode }}{% endif %}">
<i class="bi bi-filetype-csv me-1"></i>Download CSV
</a>
<a class="btn btn-outline-secondary btn-sm" href="/reports/phone-book-address?format=pdf{% for id in client_ids %}&client_ids={{ id }}{% endfor %}{% if q %}&q={{ q | urlencode }}{% endif %}{% if phone %}&phone={{ phone | urlencode }}{% endif %}">
<i class="bi bi-file-earmark-pdf me-1"></i>Download PDF
</a>
</div>
</div>
<div class="col-12">
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead class="table-light">
<tr>
<th style="width: 220px;">Name</th>
<th>Company</th>
<th>Address</th>
<th style="width: 160px;">City</th>
<th style="width: 90px;">State</th>
<th style="width: 110px;">ZIP</th>
<th style="width: 200px;">Phone</th>
</tr>
</thead>
<tbody>
{% if clients and clients|length > 0 %}
{% for c in clients %}
{% if c.phones and c.phones|length > 0 %}
{% for p in c.phones %}
<tr>
<td><span class="fw-semibold">{{ c.last_name or '' }}, {{ c.first_name or '' }}</span></td>
<td>{{ c.company or '' }}</td>
<td>{{ c.address or '' }}</td>
<td>{{ c.city or '' }}</td>
<td>{{ c.state or '' }}</td>
<td>{{ c.zip_code or '' }}</td>
<td>{{ (p.phone_type ~ ': ' if p.phone_type) ~ (p.phone_number or '') }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td><span class="fw-semibold">{{ c.last_name or '' }}, {{ c.first_name or '' }}</span></td>
<td>{{ c.company or '' }}</td>
<td>{{ c.address or '' }}</td>
<td>{{ c.city or '' }}</td>
<td>{{ c.state or '' }}</td>
<td>{{ c.zip_code or '' }}</td>
<td class="text-muted"></td>
</tr>
{% endif %}
{% endfor %}
{% else %}
<tr><td colspan="7" class="text-center text-muted py-4">No data.</td></tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}

View File

@@ -92,6 +92,15 @@
<a class="btn btn-outline-secondary" href="/reports/phone-book?format=csv{% if q %}&q={{ q | urlencode }}{% endif %}">
<i class="bi bi-filetype-csv me-1"></i>Phone Book CSV (Current Filter)
</a>
<a class="btn btn-outline-secondary js-submit-to" data-action="/reports/phone-book-address" href="#">
<i class="bi bi-journal-text me-1"></i>Phone+Address (Selected)
</a>
<a class="btn btn-outline-secondary js-submit-to" data-action="/reports/envelope" href="#">
<i class="bi bi-envelope me-1"></i>Envelope (Selected)
</a>
<a class="btn btn-outline-secondary js-submit-to" data-action="/reports/rolodex-info" href="#">
<i class="bi bi-card-text me-1"></i>Rolodex Info (Selected)
</a>
{% endcall %}
{% endif %}
</div>