Configuration
quittio/laravel reads config/quittio.php. Publish it with the installer or the publish tag:
php artisan quittio:install # config/quittio.php (see commands.md)
php artisan vendor:publish --tag=quittio-config # the same, without the hints
Without a published file the package defaults apply. Laravel merges the package file with
yours by top-level key: a section you leave out (for example preview) keeps the package
defaults, while inside a section you keep, missing keys fall back to the built-in defaults
listed below.
The manager (a singleton) reads the configuration once, when it is used for the first time;
in queue workers and under Octane that is once per worker process. After changing values at
runtime (tests, per-tenant settings) call Quittio::flush(), so that the next call builds
the core again:
config()->set('quittio.pdf.colors.primary', '#0f766e');
Quittio::flush();
seller, storage.disk and queue are read on every call; preview and
snapshots.upcasters are read when the application boots.
A value of the wrong type fails with a ConfigurationException that names the key, e.g.
quittio.pdf.pdfa must be a boolean.
The Managed layer has its own file, config/quittio-managed.php (database, issuers,
archive, queue, numbering, corrections, delivery, audit log, retention and policies); its
keys are described in the Managed layer. Issuer profiles of the Managed layer
replace the seller section below.
Environment variables
| Variable | Key | Default |
|---|---|---|
QUITTIO_COUNTRY |
defaults.country |
DE |
QUITTIO_SELLER_NAME |
seller.name |
– |
QUITTIO_SELLER_VAT_ID |
seller.vat_id |
– |
QUITTIO_SELLER_TAX_NUMBER |
seller.tax_number |
– |
QUITTIO_VALIDATION |
validation.mode |
error |
QUITTIO_GUARD |
validation.mandatory_field_guard |
– (exception, in production log) |
QUITTIO_KOSIT_URL |
validation.drivers.kosit.url |
http://127.0.0.1:8081 (start the validator with -P 8081; its own default port is 8080) |
QUITTIO_CANCELLATION_STRATEGY |
corrections.cancellation_strategy |
– (country pack) |
QUITTIO_DISK |
storage.disk |
local |
QUITTIO_PREVIEW |
preview.enabled |
– (local environment only) |
QUITTIO_PREVIEW must be true or false; other values count as not set.
Defaults
| Key | Type | Default | Effect |
|---|---|---|---|
defaults.country |
string|null | env('QUITTIO_COUNTRY', 'DE') |
Country pack used when the seller has no country (BT-40). ->country() and the seller’s country win. Without an installed pack for the code, only the generic EN 16931 rules apply. |
defaults.profile |
string|null | null |
Default profile for every format that allows it, also for ->format('zugferd') and formats chosen by a country rule; skipped where the format does not allow it (e.g. basic for XRechnung or Peppol). null = the profile of the automatic choice or the format’s default (zugferd, cii, ubl: en16931; xrechnung-cii, xrechnung-ubl: xrechnung; peppol: peppol). ->profile() always wins. See Formats and profiles. |
defaults.format |
string|null | null |
Format of a render call without ->format(). null or 'auto' = automatic choice by the format resolver (see Formats and profiles). Keys: zugferd (aliases factur-x, facturx), xrechnung-cii (alias xrechnung), xrechnung-ubl, cii, ubl, peppol, pdf, html, json. |
defaults.locale |
string|null | null |
Locale of labels and number/date formatting when neither ->locale() nor the invoice (InvoiceBuilder::locale()) sets one. null = default of the country pack (Germany: de). |
defaults.due_days |
int|numeric string|null | 14 |
Due date (BT-9) = issue date + this number of days, for invoices that set neither a due date nor payment terms; never for credit notes. null or '' switches it off; other values fail with a ConfigurationException. |
Country packs
'countries' => [
// 'DE' => \Quittio\Country\DE\CountryDE::class,
],
Country packs are found in three ways; all of them are combined:
-
Package discovery: a Composer package (or your root
composer.json) lists its packs underextra.quittio.country-packs.quittio/country-dedoes this, socomposer require quittio/country-deis enough."extra": { "quittio": { "country-packs": ["App\\Quittio\\CountryAT"] } } - Configuration:
countrieslists further pack classes (the array keys are ignored, the pack’scode()counts). Classes are resolved through the container. - At runtime:
Quittio::registerCountry(new CountryAT), e.g. in a service provider. It wins over the other two for the same country code and survivesQuittio::flush().
Every pack must implement Quittio\Core\Countries\CountryPack, otherwise building the core
fails with a ConfigurationException. For the same country code the last one wins:
discovered packs, then countries (so a subclass of CountryDE listed here replaces the
discovered CountryDE), then Quittio::registerCountry(). php artisan quittio:countries and php artisan about list
the active packs. Writing your own pack: see Country packs.
Country pack Germany (config/quittio-de.php)
The German pack has a small configuration of its own (publish it with
php artisan vendor:publish --tag=quittio-de-config):
| Key | Type | Default | Effect |
|---|---|---|---|
base_rates |
array | [] |
Base rates (§ 247 BGB) in addition to the table shipped with Quittio, ISO date (1 January or 1 July) => percent, e.g. ['2027-01-01' => '1.77']; they win over the file |
base_rate_file |
string|null | storage_path('app/quittio/base-rates.json') |
JSON file written by php artisan quittio:base-rate:update; null = not used |
See Country packs: base rate table.
Seller
Used by Seller::fromConfig() and Seller::apply() (see
Laravel integration). With quittio/managed,
issuer profiles replace it.
| Key | Type | Default | Effect |
|---|---|---|---|
seller.name |
string | env('QUITTIO_SELLER_NAME') |
Seller name (BT-27) |
seller.vat_id |
string|null | env('QUITTIO_SELLER_VAT_ID') |
VAT identifier (BT-31) |
seller.tax_number |
string|null | env('QUITTIO_SELLER_TAX_NUMBER') |
Tax number (BT-32) |
seller.legal_registration |
string|null | null |
Legal registration identifier (BT-30), e.g. HRB 12345 |
seller.identifiers |
list | [] |
Seller identifiers (BT-29): ['value' => '…', 'scheme' => null] or plain strings; empty values are skipped. Without a VAT identifier (e.g. a small business with a tax number only), EN 16931 needs BT-29 or BT-30, otherwise rendering fails with BR-CO-26. |
seller.additional_legal_info |
string|null | null |
Additional legal information (BT-33), e.g. court of registration and managing directors |
seller.address.line1 |
string|null | null |
Street and number (BT-35) |
seller.address.line2 |
string|null | null |
Additional address line (BT-36) |
seller.address.postcode |
string|null | null |
Postcode (BT-38) |
seller.address.city |
string|null | null |
City (BT-37) |
seller.address.country |
string | 'DE' |
Country code (BT-40); also selects the leading country pack |
seller.contact.name / .phone / .email |
string|null | null |
Seller contact (BG-6); added only when at least one value is set |
seller.electronic_address.value |
string|null | null |
Electronic address (BT-34); added only when set |
seller.electronic_address.scheme |
string | 'EM' |
Scheme of the electronic address (EAS code, EM = e-mail) |
seller.bank_accounts |
list | [['iban' => null, 'bic' => null, 'name' => null]] |
Seller::apply() adds a credit transfer with the first entry whose iban is not empty (bic and name optional); the other entries are ignored. |
seller.small_business |
bool | false |
Seller::apply() marks the invoice as an invoice of a small business (Germany: § 19 UStG, Meta::FLAG_SMALL_BUSINESS); country packs then reject VAT on it (Germany also the intra-community supply exemption, except for new vehicles). Use VatPreset::SmallBusiness for the lines. |
PDF and design
| Key | Type | Default | Effect |
|---|---|---|---|
pdf.renderer |
string | 'mpdf' |
PDF renderer: mpdf or a name registered with Quittio::extend('renderer', …); an unknown name fails with a ConfigurationException. |
pdf.template |
string | 'quittio::templates.default.layout' |
Blade view used for the template name default, i.e. for every render without ->template() or Presentation::$template. |
pdf.templates |
list | ['invoices.*'] |
Further views that ->template(), Presentation::$template or issuer branding may select, besides pdf.template and the shipped quittio::templates.* views. prefix* allows every view below a prefix (invoices.*, mytheme::*); partials (.partials.) are never selectable. A missing key means ['invoices.*'], [] allows no further views. See Blade templates. |
pdf.layout |
string|null | null |
Letter layout; null = country pack default (Germany: din5008b, also din5008a). An unknown layout fails with a ConfigurationException. |
pdf.paper |
string|null | null |
Paper size, e.g. A4, Letter. null = country pack default (Germany: A4); a size set here wins over the country default, also A4. The invoice presentation and ->presentation() win over it. |
pdf.pdfa |
bool | true |
Render plain PDFs (format pdf, documents) as PDF/A-3b. PDFs with embedded XML (ZUGFeRD/Factur-X) are always PDF/A-3b; watermarked PDFs (drafts, previews) never. |
pdf.font |
string | 'inter' |
Default font family (plain family name). Inter is bundled. |
pdf.fonts |
array | [] |
Additional fonts for mPDF: lower-case family => ['R' => file, 'B' => file, 'I' => file, 'BI' => file], files relative to pdf.font_directories. |
pdf.font_directories |
list | [] |
Directories mPDF searches for the font files of pdf.fonts, after its own and the bundled Inter. resources/fonts/quittio and its subdirectories (where --tag=quittio-fonts publishes Inter, to resources/fonts/quittio/inter) are added after the configured directories when they exist, so fonts placed there need no entry here. mPDF takes a file from the first directory that has it; the bundled Inter comes before all of these. |
pdf.logo |
string|null | null |
Logo: absolute path, http(s) URL or data URI of a PNG, JPEG or SVG; at most 10 MB and 25 megapixels, URLs only from public hosts. For PDF/A logos are flattened to an RGB PNG; SVG needs the imagick extension. Details in Templates and design. |
pdf.colors.primary |
string | '#1e40af' |
Primary colour (hex) |
pdf.colors.text |
string | '#111827' |
Text colour (hex) |
pdf.colors.muted |
string | '#6b7280' |
Colour of secondary text (hex) |
pdf.colors.secondary |
string|null | not in the file (null) |
Secondary colour (design.secondaryColor); null = the primary colour |
pdf.sender_line |
string|null | null |
Sender line above the address field; null = generated from the seller |
pdf.footer_columns |
list | [] |
Footer columns, each a list of lines: [['Muster GmbH', 'Hauptstraße 1'], ['IBAN DE02 …']]. [] = footer generated from the seller data. |
pdf.texts.salutation |
string|null | null |
Salutation above the intro text; null = none |
pdf.texts.intro |
string|null | null |
Text above the lines (Markdown, PDF only) |
pdf.texts.closing |
string|null | null |
Text below the totals (Markdown, PDF only) |
pdf.show_item_numbers |
bool | false |
Print the seller’s item number (BT-155) next to the item name |
pdf.markdown |
bool | false |
Render line descriptions and notes as Markdown instead of plain text. They are also in the e-invoice XML, and formatting can change the displayed text (e.g. * for emphasis), so it is off by default. |
pdf.fold_marks |
bool | false |
Print fold and punch marks |
pdf.payment_qr.type |
string | 'auto' |
Payment QR code: auto (the generator below, otherwise the code of the country pack, Germany: GiroCode), girocode (EPC QR code), swiss-qr-bill (needs pdf.payment_qr.generator; Quittio ships no Swiss QR bill generator, without one rendering fails with a ConfigurationException) or none. Unknown values fail with a ConfigurationException. Refunds and self-billed invoices never get a code. |
pdf.payment_qr.generator |
class-string|null | null |
Class implementing Quittio\Core\Pdf\Qr\PaymentQrGenerator, resolved from the container: used for auto before the country pack’s generator and required for swiss-qr-bill. Another class fails with a ConfigurationException. |
pdf.payment_qr.girocode_amount |
string | 'due' |
Amount in the GiroCode: due (amount due) or discounted (amount after the first cash discount; the PDF then notes until when it is valid). |
pdf.filename |
string | '{title}_{number}.{ext}' |
File name pattern of the results. Placeholders: {title} (document title, e.g. “Rechnung”), {number}, {date} (issue date, Y-m-d), {format} (format key; document type for quotes and other documents), {ext} (pdf, xml, html, json). Title and number are reduced to letters, digits, ., _ and -. |
pdf.cache_path |
string|null | storage_path('framework/cache/quittio') |
Temporary directory of mPDF; prepared logos are cached in its logos subdirectory. null = sys_get_temp_dir().'/quittio-mpdf'. |
pdf.renderers.mpdf.simple_tables |
bool | false |
mPDF simpleTables: drops the borders of table cells and is not faster. Keep it off. |
pdf.renderers.mpdf.pdfa_auto |
bool | false |
mPDF PDFAauto: let mPDF correct PDF/A violations itself instead of failing |
pdf.renderers.{name} |
array | – | Options passed to the factory of a renderer registered with Quittio::extend('renderer', '{name}', …) |
The design keys (logo, colors, font, sender_line, layout, texts,
footer_columns, show_item_numbers, payment_qr.type, paper) are the design level 1
of Templates and design; the invoice presentation and ->presentation()
override them property by property.
Validation
| Key | Type | Default | Effect |
|---|---|---|---|
validation.mode |
string | env('QUITTIO_VALIDATION', 'error') |
error: throw a ValidationException on errors; warn: attach the result only; off: no validation while rendering. Per call: ->validate(), ->validate(false), ->withoutValidation(). |
validation.levels |
list | ['php', 'xsd'] |
Validation run while rendering and by validateFile(): php (L1–L3, L7), xsd (L4), kosit (L5, KoSIT validator), verapdf (L6), or the name of a driver registered with Quittio::extend('validator', …). Without php, the PHP rules do not run. |
validation.block_on_warnings |
bool | false |
In mode error, warnings block as well |
validation.mandatory_field_guard |
string|null | env('QUITTIO_GUARD') |
Check that the template printed every mandatory value: exception, log or off. null = exception outside production, log in production. |
validation.drivers.kosit.mode |
string | 'daemon' |
daemon (HTTP to a running validator) or cli (one JVM per file) |
validation.drivers.kosit.url |
string | env('QUITTIO_KOSIT_URL', 'http://127.0.0.1:8081') |
Address of the validator daemon. The validator listens on port 8080 unless started with -P 8081 (or mapped, docker run -p 8081:8080); quittio:schemas:download prints a start command for the port of this URL. |
validation.drivers.kosit.jar |
string|null | null |
Validator JAR (mode cli) |
validation.drivers.kosit.scenarios |
string|null | null |
Scenario file of the main configuration (mode cli, e.g. XRechnung) |
validation.drivers.kosit.repository |
string|null | null |
Repository directory of the main configuration (mode cli) |
validation.drivers.kosit.configurations |
list | [] |
Further scenario sets ['scenarios' => …, 'repository' => …] (mode cli), e.g. Peppol BIS; quittio:schemas:download prints the values |
validation.drivers.kosit.java |
string | 'java' |
Java binary (mode cli) |
validation.drivers.kosit.timeout |
int | 30 |
Timeout in seconds |
validation.drivers.kosit.fail_on_unavailable |
bool | true |
An unreachable validator is an error (KOSIT-UNAVAILABLE); false = a warning |
validation.drivers.verapdf.mode |
string | 'cli' |
cli (local verapdf binary) or docker (image, the PDF is passed via stdin) |
validation.drivers.verapdf.binary |
string | 'verapdf' |
veraPDF binary (mode cli) |
validation.drivers.verapdf.image |
string | 'verapdf/cli' |
Docker image (mode docker) |
validation.drivers.verapdf.docker |
string | 'docker' |
Docker binary (mode docker) |
validation.drivers.verapdf.timeout |
int | 120 |
Timeout in seconds |
validation.drivers.verapdf.fail_on_unavailable |
bool | true |
An unavailable veraPDF is an error; false = a warning |
validation.drivers.vies.enabled |
bool | false |
Check VAT identifiers online with the EU VIES service (QUITTIO-VIES, QUITTIO-VIES-UNAVAILABLE). A Quittio\Core\Validation\Plausibility\VatIdVerifier bound in the container replaces the VIES client (e.g. in tests). |
validation.drivers.vies.cache_ttl |
int | 86400 |
Seconds a VALID or INVALID answer is cached (in memory, per process) |
validation.drivers.vies.timeout |
int | 5 |
Timeout in seconds |
validation.drivers.{name} |
array | – | Options passed to the factory of a validator registered with Quittio::extend('validator', '{name}', …) |
The KoSIT and veraPDF drivers run only when kosit and verapdf are listed in
validation.levels; quittio:validate --kosit --verapdf adds them for one file with the same
settings. Levels, messages and the official validators: see Validation.
Corrections
| Key | Type | Default | Effect |
|---|---|---|---|
corrections.cancellation_strategy |
string|null | env('QUITTIO_CANCELLATION_STRATEGY') |
Sign strategy of Quittio::cancellation() and of Managed cancellations: credit_note (type code 381, positive amounts) or negative_invoice (type code 380, negative amounts). null = default of the country pack (Germany: credit_note). The strategy: argument wins. An unknown value fails with a ConfigurationException. |
Snapshots
| Key | Type | Default | Effect |
|---|---|---|---|
snapshots.upcasters |
list | [] |
Classes (resolved through the container) or instances implementing Quittio\Core\Serialization\Snapshots\SnapshotUpcaster. They bring stored invoice snapshots of older data model versions up to date when they are read: InvoiceData::fromSnapshot(), queued RenderInvoice jobs and the invoices of the Managed layer. Other values fail with a ConfigurationException. |
An upcaster reads one version and returns the next one:
use Quittio\Core\Serialization\Snapshots\SnapshotUpcaster;
final class RenameReferenceField implements SnapshotUpcaster
{
public function from(): int
{
return 1; // produces version 2
}
public function upcast(array $snapshot): array // without the "_version" key
{
$snapshot['buyerReference'] ??= $snapshot['reference'] ?? null;
unset($snapshot['reference']);
return $snapshot;
}
}
Reader
Limits for reading incoming files (Quittio::read(), readFile(), validateFile(),
validatePath(), quittio:read, quittio:validate); see Reading incoming invoices.
| Key | Type | Default | Effect |
|---|---|---|---|
reader.max_xml_bytes |
int | 10 * 1024 * 1024 |
Largest XML file (bytes) |
reader.max_pdf_bytes |
int | 25 * 1024 * 1024 |
Largest PDF file (bytes) |
reader.max_decompressed_bytes |
int | 50 * 1024 * 1024 |
Largest decompressed content of the PDF streams (bytes) |
Format resolver
| Key | Type | Default | Effect |
|---|---|---|---|
format_resolver.driver |
string | 'default' |
default (the built-in choice, see Formats and profiles) or the name of a resolver registered with Quittio::extend('format_resolver', …); the factory receives the whole format_resolver array. An unknown name fails with a ConfigurationException. |
format_resolver.peppol_lookup |
class-string|null | null |
Peppol lookup of the default resolver: an invokable class, resolved from the container, that receives the Quittio\Core\Data\Party\BuyerParty and returns true when the buyer is reachable via Peppol; the automatic choice then picks peppol for business buyers that no domestic country rule decided (see Peppol lookup). At runtime a callable works as well. Anything else fails with a ConfigurationException. null = no lookup. |
format_resolver.b2c_embed_xml |
bool | false |
Embed a CII XML (factur-x.xml, profile from defaults.profile) also into plain PDFs (format pdf, e.g. the automatic choice for consumers); these PDFs are then PDF/A-3b. |
format_resolver.auto_upgrade_profile |
bool | true |
When an invoice uses EXTENDED features (e.g. sub lines) and no profile was requested, CII formats switch to the profile EXTENDED; false = fail with a ConfigurationException instead. |
Published files of earlier versions may contain a networks section (mail, Peppol, French
platform); nothing reads it, so remove it. The Peppol lookup above replaces
networks.peppol.
Storage, queue and preview
| Key | Type | Default | Effect |
|---|---|---|---|
storage.disk |
string | env('QUITTIO_DISK', 'local') |
Filesystem disk of ->save() on rendered results and of queued renders without a disk. Empty = the default disk of config/filesystems.php. The archive of the Managed layer uses quittio-managed.disk. |
queue.connection |
string|null | null |
Queue connection of ->queue(); null = the default connection |
queue.queue |
string|null | 'quittio' |
Queue name of ->queue(). Workers must listen on it: php artisan queue:work --queue=quittio,default. |
preview.enabled |
bool|null | env('QUITTIO_PREVIEW', null) |
Register the browser preview route; null = only in the local environment. Never registered while routes are cached (route:cache). |
preview.path |
string | '_quittio/preview' |
URL prefix of the preview |
preview.middleware |
list | ['web'] |
Middleware of the preview routes, e.g. ['web', 'auth'] when you enable it outside local |
See Preview route.
Serializer extensions
serializers.{name} (not in the published file) holds the options passed to the factory of a
serializer registered with Quittio::extend('serializer', '{name}', …). Registered
serializers are always used and replace the built-in writer of their syntax (CII or UBL).
Without Laravel
The core takes the same settings as Quittio\Core\Config or as an array for
Quittio::create() (Config::fromArray()); some keys are shaped differently there (e.g.
validation.levels as L1 … L7, pdf.b2c_embed_xml, format instead of
defaults.format). See
Getting started.