Document Types
Document Types
Easy PDF Invoices for WooCommerce generates four document types and decides which one to render based on order status. This page explains each type, when it appears, and how to override the default behaviour.
The four types
Invoice
Used for orders awaiting payment.
- Title: Invoice
- No status badge
- Shows Amount Due
- Includes terms and conditions text (configurable on the Legal tab)
- Triggered by: pending, on-hold, processing (when payment hasn't been confirmed by the gateway)
Receipt
Used for completed orders that have been paid.
- Title: Receipt
- PAID badge in green, top right
- Caption: "Paid on [date] via [gateway]"
- Shows the payment date and method below the badge
- No terms section
- Triggered by: completed, processing (when
$order->is_paid()returns true)
Credit Note
Used for full or partial refunds.
- Title: Credit Note
- REFUNDED badge in red, top right
- References the original invoice number
- Shows refund total as a line item
- Separate sequential numbering (e.g.,
CN-2026-000001) - Triggered by: refunded status, partial refund event
Packing Slip
Used for fulfilment.
- Title: Packing Slip
- No prices, no tax, no payment information
- Includes: product name, SKU, quantity, weight (if available), order notes
- Ship-to address prominently displayed
- Optional QR code with order number for warehouse scanning (off by default)
- Not auto-generated — created on demand from the admin
How the plugin decides
The plugin reads order status and uses this resolution order:
| Order status | Document type | Notes |
|---|---|---|
| Pending | Invoice | Awaiting payment |
| On Hold | Invoice | Awaiting confirmation |
| Processing (payment not confirmed) | Invoice | Order received but payment pending |
| Processing (payment confirmed) | Receipt | $order->is_paid() returned true |
| Completed | Receipt | Always shows PAID badge |
| Refunded | Credit Note | REFUNDED badge, references original |
| Partial refund | Receipt | With a refund line item shown in totals |
| Failed | No document | Plugin skips generation |
| Cancelled | No document | Plugin skips generation |
DocumentFactory::resolve_type(). Refunded → credit-note, paid → receipt, otherwise invoice. Failed and cancelled orders are skipped entirely.
Overriding the resolution
You can override the resolution per-order with the epdi_document_type filter:
add_filter( 'epdi_document_type', function( $type, $order ) {
// Always render proforma-style invoices for B2B orders
if ( $order->get_meta( '_b2b_proforma' ) === 'yes' ) {
return 'invoice';
}
return $type;
}, 10, 2 );
You can also force a global default in WooCommerce > Settings > Invoices > General > Document type mode:
- Auto (recommended) — uses the resolution table above.
- Always Invoice — every order generates an Invoice, regardless of payment status.
- Always Receipt — every order generates a Receipt with PAID badge.
When documents are generated
PDFs are generated lazily — the plugin doesn't burn CPU rendering documents that no one will read.
| Trigger | What happens |
|---|---|
| Order status changes | If type changed, regenerate. Otherwise reuse. |
| WooCommerce sends an email with attach | Generate if missing, attach. |
| Customer downloads from My Account | Generate if missing, stream the file. |
| Customer downloads from Thank You page | Same as My Account. |
| Admin clicks Download in the metabox | Stream existing or regenerate if forced. |
| Admin clicks Regenerate | Always regenerate, replacing the previous file. |
| Bulk action on the order list | Regenerate each order, inline up to 50, queued above. |
StatusListener) only regenerates when the resolved type differs from what's already stored. If an order moves from Processing to Completed and both resolve to Receipt, the existing PDF is reused. If it moves from Processing (Invoice) to Completed (Receipt), a fresh PDF is generated.
Number persistence across transitions
Once an invoice number is assigned to an order, it stays. If the order's document type changes (Invoice → Receipt → Credit Note), the same number is reused so your records stay clean.
For credit notes specifically, a separate sequential counter is used so credit note numbers don't collide with invoice numbers.
Refunds and partial refunds
When you process a refund through WooCommerce:
- Full refund (status moves to "Refunded") → Credit Note is generated automatically.
- Partial refund (refund recorded, status stays the same) → The current document (usually Receipt) regenerates with a refund line item shown in the totals section.
customer_refunded_order and customer_partially_refunded_order email IDs so the right attachment goes out.
Packing slips on demand
Packing slips don't follow the auto-generation flow. They're created manually:
- From the order edit screen, click Generate packing slip in the metabox.
- From the order list, select multiple orders and use the Generate packing slips (ZIP) bulk action.
- Optionally, enable Auto-attach packing slip to Customer Completed Order email in the Emails tab.
Hooks for developers
// Fires when document type is resolved (good for logging or analytics)
do_action( 'epdi_document_type_resolved', $order, $document_type );
// Filter the resolved type (per-order override)
apply_filters( 'epdi_document_type', $type, $order );
// Prevent generation entirely for specific orders
apply_filters( 'epdi_should_generate', $should_generate, $order );
// Fires before PDF generation
do_action( 'epdi_before_generate', $order, $document_type );
// Fires after PDF generation
do_action( 'epdi_after_generate', $order, $document_type, $pdf_path );
See the Developer Guide for the full list of hooks and filters.