# Hooks - Folioline 1.x documentation

> Folioline's actions and filters, its templates, the document data they get, and the WP-CLI commands.

Put the examples in a small custom plugin or your child theme's `functions.php`. Folioline's classes live in the `Extendiment\Folioline` namespace.

## The data hooks get

A **document** (`$document` in admin, file and email hooks) is an object with `id`, `type` (`invoice` or `credit_note`), `series`, `sequence`, `number`, `order_id`, `refund_id`, `parent_id` (a credit note's invoice), `issue_date` (`Y-m-d`), `currency`, `net_total`, `tax_total`, `total`, `country`, `vat_number`, `reverse_charge` (bool), `data`, `created_by` and `created_at`.

`data` is the **snapshot**: everything the document says, frozen when it was issued. PDF and e-invoice hooks get it as an array (`$document` or `$snapshot`):

| Key | |
|---|---|
| `type`, `number`, `issue_at`, `currency`, `decimals`, `language` | The document itself. |
| `order` | `id`, `number`, `date`, `paid_date`, `payment_method`, `payment_method_title`, `transaction_id`, `customer_id`. |
| `delivery_date`, `due_date`, `paid` | `due_date` is `null` when paid. |
| `seller` | `company`, address keys, `vat_id`, `tax_number`, `registration`, `email`, `phone`, `bank_name`, `iban`, `bic`, `logo_id`, `footer`. |
| `buyer` | `name`, `company`, address keys, `email`, `phone`, `vat_number`, `vat_status`, `vat_consultation`, `vat_checked`, `customer_id`. |
| `shipping` | Address keys, or `null`. |
| `tax_country` | The country the VAT was charged for (the OSS report's country). A credit note has its invoice's. Not on documents issued before 1.1.2. |
| `reverse_charge`, `notices` | The legal wording printed for the tax categories. |
| `lines` | `kind` (`product`, `shipping`, `fee`, `refund`), `name`, `sku`, `product_id`, `quantity`, `unit_net`, `net`, `discount`, `tax`, `gross`, `rate`, `category`. |
| `taxes` | Per category and rate: `category`, `rate`, `net`, `tax`. |
| `totals` | `net`, `tax`, `gross`, `discount`, `rounding`. |
| `invoice`, `reason` | A credit note's invoice (`id`, `number`, `date`) and refund reason. |
| `evidence` | `ip`, `ip_country`, `billing_country`, or empty. |

Amounts are in the order's currency, rounded to its decimals; a credit note's amounts are positive. Tax categories are the EN 16931 codes: `S` taxed, `Z` zero-rated, `E` exempt, `AE` reverse charge, `K` intra-EU supply of goods, `G` export of goods, `O` outside the scope of EU VAT.

E-invoice formats are `zugferd`, `xrechnung-cii`, `xrechnung-ubl` and `peppol`; file hooks also use `pdf`.

## Actions

| Action | When |
|---|---|
| `folioline_loaded` | Folioline has loaded (on `plugins_loaded`, with WooCommerce active). Its classes and `FOLIOLINE_*` constants are available. |
| `folioline_activated` | Folioline was activated. |
| `folioline_deactivated` | Folioline was deactivated. |
| `folioline_settings` | The settings page is built (`$page`, the page slug `folioline`): add sections and fields with the Settings API. Sections of your own show on a **More** tab. |
| `folioline_document_issued` | A document was issued and has its number (`$document, $order`). |
| `folioline_file_stored` | A document's file was made and stored (`$path, $document, $format`): the first time it's needed, again after it went missing, or with a new **Buyer reference** on the order (a new file; the earlier one is kept). |
| `folioline_documents_tab` | Print the contents of a tab you added to **WooCommerce → Invoices** (`$tab`). |
| `folioline_documents_column` | Print a cell of a column you added to the Invoices list (`$column, $document`). |

```php
// Copy every new document's PDF to an archive folder.
add_action( 'folioline_file_stored', function ( $path, $document, $format ) {
	if ( 'pdf' === $format ) {
		copy( $path, '/mnt/archive/' . basename( $path ) ); // the stored name: a new buyer reference never overwrites an earlier file
	}
}, 10, 3 );
```

## Filters

### Issuing and numbering

| Filter | What it's for |
|---|---|
| `folioline_wants_invoice` | Whether an order that reaches an invoice status gets its invoice automatically (`$wants, $order`). Staff can still issue one by hand. |
| `folioline_document_data` | The snapshot before it's frozen (`$data, $type, $order, $refund`; `$refund` is `null` for invoices). |
| `folioline_document_number` | The printed number (`$number, $type, $sequence, $date`), up to 80 characters. The series and its sequence don't change. |
| `folioline_tax_category` | A line's tax category (`$category, $tax, $virtual, $context`). `$context` has `reverse`, `physical`, and the countries `seller`, `buyer` (billing) and `destination` (where goods go: the shipping address's, the store's for local pickup, else the billing address's). |
| `folioline_due_date` | When an unpaid invoice is due (`$due, $order, $issued`; Y-m-d dates). Never before the invoice's date. Wholesaleline's orders on account use it. |
| `folioline_notices` | The legal wording printed on a document (`$notices, $categories`). |

```php
// No automatic invoices for wholesale orders: staff issue them by hand.
add_filter( 'folioline_wants_invoice', function ( $wants, $order ) {
	return $wants && ! $order->get_meta( '_is_wholesale' );
}, 10, 2 );
```

### VAT

| Filter | What it's for |
|---|---|
| `folioline_vat_exempt` | Whether VAT comes off for a customer (`$exempt, $number, $country, $status, $reason, $delivery`). `$status` is `valid`, `invalid`, `pending` (VIES unreachable) or `unchecked`. `$reason` says why a number can't take VAT off (`format`, `domestic`, `non_eu`, `mismatch`, `seller`, `delivered`), '' when it can. `$delivery` is the country the goods are delivered to, '' when there are none. |
| `folioline_vies_result` | VIES's answer before Folioline uses and caches it (`$result, $number`): `status` (`valid`, `invalid`, `unavailable`), `number`, `name`, `address`, `consultation`, `checked` (UTC), `error`, `cached`. |

### PDFs

| Filter | What it's for |
|---|---|
| `folioline_pdf_template` | The PHP template a PDF is made from (`$template, $document`): an absolute path. |
| `folioline_pdf_css` | The PDF's stylesheet (`$css, $document`), in mPDF's CSS subset. |
| `folioline_mpdf_config` | mPDF's configuration (`$config, $document`). Keep `PDFA` and `PDFAversion` as they are. |
| `folioline_pdf_embed_xml` | Whether PDFs carry the Factur-X XML (`$embed, $document`); default `true`. |
| `folioline_missing_extensions` | The PHP extensions counted as missing for PDFs (`$missing`). |

### E-invoices

| Filter | What it's for |
|---|---|
| `folioline_buyer_reference` | The buyer reference, BT-10 (`$reference, $snapshot, $format`); default the **Buyer reference** typed on the order, else the order number. |
| `folioline_electronic_address` | A party's electronic address, BT-34 / BT-49 (`$endpoint, $role, $snapshot, $format`): `[ 'scheme' => …, 'id' => … ]`. `$role` is `seller` or `buyer`. Default: for Peppol, the VAT number under its country's scheme; otherwise the email address (scheme `EM`). |
| `folioline_payment_means` | Payment instructions (`$payment, $snapshot, $format`): `code` (UNCL4461), `name`, `reference`, `iban`, `bic`, `account_name`, `terms`. |
| `folioline_einvoice_model` | All the business terms before the XML is written (`$model, $snapshot, $format`). |
| `folioline_einvoice_problems` | What keeps a document from being written in a format (`$problems, $snapshot, $format`): messages for the store owner; any message stops the file. |

```php
// A Leitweg-ID a checkout field plugin keeps on the order, when staff didn't type one.
add_filter( 'folioline_buyer_reference', function ( $reference, $snapshot ) {
	$order = wc_get_order( $snapshot['order']['id'] );
	if ( ! $order || '' !== $order->get_meta( '_folioline_buyer_reference' ) ) {
		return $reference;
	}
	$id = $order->get_meta( 'leitweg_id' );
	return $id ? $id : $reference;
}, 10, 2 );

// A Peppol participant ID (GLN) for the seller.
add_filter( 'folioline_electronic_address', function ( $endpoint, $role, $snapshot, $format ) {
	if ( 'seller' === $role && 'peppol' === $format ) {
		return array( 'scheme' => '0088', 'id' => '4035811991021' );
	}
	return $endpoint;
}, 10, 4 );
```

The buyer reference and electronic address are worked out when a file is made, so they apply to files made after you add the filter. Changing **Buyer reference** on an order gives its documents new files, and keeps the earlier ones.

### Emails and downloads

| Filter | What it's for |
|---|---|
| `folioline_email_documents` | The documents attached to a WooCommerce email (`$documents, $email_id, $order, $email`). |
| `folioline_can_download` | Whether a user may download a document's files (`$allowed, $document, $user_id`). By default: staff who can edit orders or manage WooCommerce, and the order's own logged-in customer while My Account downloads are on. |
| `folioline_admin_document_formats` | The formats linked next to a document on the admin screens (`$formats, $document`). Default: `pdf` and the e-invoice format chosen for emails, or `xrechnung-cii`. |

### Admin screens and reports

| Filter | What it's for |
|---|---|
| `folioline_settings_fields` | The fields on **WooCommerce → Folioline** (`$fields`). A field's key must be one Folioline's settings keep. |
| `folioline_documents_tabs` | The tabs on **WooCommerce → Invoices** (`$tabs`, tab => label). Print yours on `folioline_documents_tab`. |
| `folioline_documents_columns` | The columns of the Invoices list (`$columns`). Print yours on `folioline_documents_column`. |
| `folioline_register_row` | A row of the one-row-per-document register (`$row, $document`). |
| `folioline_oss_rows` | The OSS report's rows (`$rows, $from, $to`): `country`, `rate`, `net`, `tax` in euros. |
| `folioline_csv_delimiter` | The CSV separator (`$delimiter`): `,` by default, or `;`, a tab or `|`. |

```php
// Semicolon-separated CSV files for Excel in Germany, France or Italy.
add_filter( 'folioline_csv_delimiter', function () {
	return ';';
} );
```

## Templates

Copy a template into your theme to change it, as for WooCommerce's own templates:

| Template | Copy to |
|---|---|
| `templates/document.php`: the invoice and credit note PDF. It gets `$document` (the snapshot) and `$view` (formatting helpers). Print only what's in `$document`, never the order, so the PDF reads the same whenever it's made. | `your-theme/folioline/document.php` |
| `templates/myaccount/documents.php`: the Documents table in My Account. | `your-theme/folioline/myaccount/documents.php` |

The PDF's styles are in `assets/css/document.css` (filter `folioline_pdf_css`).

## Functions

`Extendiment\Folioline\Documents` reads and issues documents: `get( $id )`, `invoice_for( $order_id )`, `credit_note_for( $order_id, $refund_id )`, `for_order( $order_id )`, `query( $args )`, `issue_invoice( $order )` and `issue_credit_note( $order, $refund )`. The issue methods return the existing document when there is one, so they're safe to call twice. `Extendiment\Folioline\Output\Files::pdf( $document )` and `::xml( $document, $format )` return a stored file's path (or a `WP_Error` saying why it can't be made).

## WP-CLI

| Command | |
|---|---|
| `wp folioline issue <order>...` | Issues each order's invoice, and credit notes for its refunds, if it has no invoice yet. |
| `wp folioline list [--type=<type>] [--from=<Y-m-d>] [--to=<Y-m-d>] [--format=<table\|csv\|json>]` | Lists documents. `--type` is `invoice` or `credit_note`. |
| `wp folioline render <document>... [--format=<format>]` | Prints a document's stored file path, making it first if needed. `--format`: `pdf` (default), `zugferd`, `xrechnung-cii`, `xrechnung-ubl` or `peppol`. |

Source: https://extendiment.com/plugins/folioline/docs/1.x/hooks
