Add get_statement_data (open invoices, aging buckets, totals, formatted customer + company addresses) and generate_statements, which renders one page per customer via a Jinja template and returns a printable HTML document. Recipient window geometry (top:1.9in/left:1.125in) mirrors the existing double-window print formats for #10 envelope compatibility; each page uses page-break-after:always. Late fee is a zero placeholder here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
101 lines
3.7 KiB
HTML
101 lines
3.7 KiB
HTML
{# One customer account statement = one printed page.
|
||
Envelope geometry (recipient window at top:1.9in / left:1.125in) mirrors the
|
||
existing double-window print formats so the same #10 double-window envelopes
|
||
work. Rendered via frappe.render_template with context key `s`
|
||
(see ns_app.api.statements.get_statement_data). #}
|
||
{% set fmt = frappe.utils.fmt_money %}
|
||
<div class="statement-page">
|
||
|
||
<!-- Return address (top-left envelope window) -->
|
||
<div class="return-window">
|
||
<strong>{{ s.company_name }}</strong><br>
|
||
{{ s.return_address | safe }}
|
||
</div>
|
||
|
||
<!-- Document header (top-right) -->
|
||
<div class="doc-header">
|
||
<div class="doc-title">STATEMENT</div>
|
||
<div><strong>Date:</strong> {{ frappe.utils.formatdate(s.statement_date, "MM-dd-yyyy") }}</div>
|
||
<div><strong>Account:</strong> {{ s.customer }}</div>
|
||
</div>
|
||
|
||
<!-- Recipient address (lower envelope window) -->
|
||
<div class="recipient-window">
|
||
{{ s.customer_name }}<br>
|
||
{{ s.customer_address | safe }}
|
||
</div>
|
||
|
||
<!-- Statement body (starts below the address windows) -->
|
||
<div class="statement-body">
|
||
|
||
<div class="intro">
|
||
The following is a summary of your account as of
|
||
{{ frappe.utils.formatdate(s.statement_date, "MM-dd-yyyy") }}.
|
||
Please remit payment for any past-due balance at your earliest convenience.
|
||
</div>
|
||
|
||
<table class="items">
|
||
<thead>
|
||
<tr>
|
||
<th>Invoice</th>
|
||
<th class="c">Date</th>
|
||
<th class="c">Due Date</th>
|
||
<th class="c">Days Overdue</th>
|
||
<th class="c">Aging</th>
|
||
<th class="r">Outstanding</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for inv in s.invoices %}
|
||
<tr class="{{ 'overdue' if inv.is_overdue else '' }}">
|
||
<td>{{ inv.name }}</td>
|
||
<td class="c">{{ frappe.utils.formatdate(inv.posting_date, "MM-dd-yyyy") }}</td>
|
||
<td class="c">{{ frappe.utils.formatdate(inv.due_date, "MM-dd-yyyy") }}</td>
|
||
<td class="c">{{ inv.days_overdue if inv.days_overdue else "—" }}</td>
|
||
<td class="c">{{ inv.aging_bucket }}</td>
|
||
<td class="r">{{ fmt(inv.outstanding_amount, currency=s.currency) }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- Totals -->
|
||
<div class="totals">
|
||
<p><span>Total Outstanding:</span><span>{{ fmt(s.total_outstanding, currency=s.currency) }}</span></p>
|
||
{% if s.late_fee and s.late_fee > 0 %}
|
||
<p><span>Late Payment Fee:</span><span>{{ fmt(s.late_fee, currency=s.currency) }}</span></p>
|
||
{% endif %}
|
||
<p class="grand"><span>Total Due:</span><span>{{ fmt(s.total_due, currency=s.currency) }}</span></p>
|
||
</div>
|
||
|
||
<!-- Aging summary -->
|
||
<table class="aging">
|
||
<thead>
|
||
<tr>
|
||
<th class="c">Current</th>
|
||
<th class="c">1–30</th>
|
||
<th class="c">31–60</th>
|
||
<th class="c">61–90</th>
|
||
<th class="c">90+</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="c">{{ fmt(s.aging["Current"], currency=s.currency) }}</td>
|
||
<td class="c">{{ fmt(s.aging["1-30"], currency=s.currency) }}</td>
|
||
<td class="c">{{ fmt(s.aging["31-60"], currency=s.currency) }}</td>
|
||
<td class="c">{{ fmt(s.aging["61-90"], currency=s.currency) }}</td>
|
||
<td class="c">{{ fmt(s.aging["90+"], currency=s.currency) }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="footer">
|
||
Prompt payment is always appreciated. We accept payments by check or over
|
||
the phone using a debit or credit card. Automatic payment setup is also
|
||
available upon request. Please contact us if payment has already been sent.
|
||
</div>
|
||
|
||
</div><!-- /statement-body -->
|
||
</div><!-- /statement-page -->
|