How-to guide
How to automate monthly statements
Create a repeatable statement workflow that turns account activity exports into customer-ready monthly PDFs.
What you'll need
Supplies
- Monthly account activity export
- Statement template copy
- Balance reconciliation notes
Tools
- DocForge
- Billing or accounting system
- Spreadsheet app
Steps
- 1
Freeze the statement period
Freeze the period before exporting, then do not regenerate from a moving report. A good statement CSV includes `account_number,customer_name,statement_start,statement_end,opening_balance,new_charges,payments,credits,closing_balance,due_date,support_email`. Export after late adjustments, credits, and payment reversals are posted, otherwise the PDF batch will disagree with the reconciled ledger. Use exact dates such as `statement_start` of `2026-06-01` and `statement_end` of `2026-06-30`; avoid labels like `June` because the template cannot infer the year reliably. If your finance system exports parentheses for credits, such as `(25.00)`, normalize that to `-25.00` or a pre-formatted credit string before upload. Preview accounts with zero balances, credit balances, and recent payments posted on the last day of the period. Those rows expose whether statement language says `Amount due` when it should say `No payment due` or `Credit on account`.
- 2
Map balances to the template
Map balances slowly and use the same terminology finance uses in reconciliation. `opening_balance` should feed the beginning balance, `new_charges` should feed period charges, `payments` should reduce the balance, `credits` should reduce it separately, and `closing_balance` should be the final amount shown. Do not map `total_due` to `closing_balance` unless those columns are defined the same way in the export. A useful summary line is `{{ row.opening_balance }} + {{ row.new_charges }} - {{ row.payments }} - {{ row.credits }} = {{ row.closing_balance }}`, but only if the values are already formatted for customers. For optional support fields, use stock Liquid fallbacks such as `{{ row.support_email | default: 'billing@example.com' }}`. Add conditional language for credits: `{% if row.closing_balance contains '-' %}This account has a credit balance.{% endif %}` if your exported values are pre-formatted strings. Preview the wording carefully, because negative amounts are where statement templates most often sound wrong.
- 3
Preview reconciled examples
Preview a controlled sample set against the reconciled export. Use one normal account, one high balance, one zero balance, one credit balance, one recent payment, and one long organization name like `Northwind Regional Healthcare Purchasing Cooperative`. Check the arithmetic visually: `opening_balance` of `500.00`, `new_charges` of `300.00`, `payments` of `200.00`, and `credits` of `0.00` should produce a `closing_balance` of `600.00`. The template does not need to calculate that total if finance has already reconciled it, but the displayed story must make sense. If you do calculate small presentation values, keep Liquid simple, such as `{{ row.new_charges | round: 2 }}`. Watch for date fields that cross month boundaries, especially payments posted on `2026-06-30`. A payment included in the CSV should appear in the statement period. If customers see a payment on the PDF but the closing balance ignores it, trust in the statement drops immediately.
- 4
Generate and archive the run
Generate the monthly batch only after finance approves the preview set. Archive the ZIP, source CSV, and template version together under a date-stamped name such as `statements_2026-06`. Keep row-level errors with the run notes, even after correction, so support can explain why an account received a late or regenerated statement. Use stable file naming based on `account_number` and `statement_end`, for example `ACCT-10482_2026-06-30.pdf`. After generation, spot-check the first PDF, last PDF, a zero-balance PDF, and any account with a credit or unusually large charge. If routing statements through a downstream system, pass the same identifiers that appear on the PDF: `account_number`, `customer_name`, and `statement_end`. Do not rely on row number as the archive key. Six months later, the useful audit trail is "this PDF came from this approved CSV and this template," not "this was row 913 in an export someone replaced."