Repository navigation
Releases: Quittio/quittio
Releases · Quittio/quittio
Release list
0.5.0
Added
MimeType::Xml(application/xml) for supporting documents (BT-125-1) in the profilexrechnung-extension, which allows XML attachments (BR-DEX-01). The PHP rules now check the MIME code withBR-CL-24: other profiles reportMimeType::Xmlas error, as the official Schematron does (BR-CL-24, in Peppol alsoPEPPOL-EN16931-CL001); the extension accepts it throughExtendsCodeLists::additionalCodes().
Changed
- The VAT rows of the PDF and HTML name IGIC (category L, Canary Islands) and IPSI (category M, Ceuta and Melilla) instead of VAT, e.g. "7 % IGIC auf 50,00 €" or "7% IGIC on 50.00 €" (labels
vat_row_igicandvat_row_ipsi). Rows of the other categories are unchanged. - The package rules
QUITTIO-DECLARED-AMOUNTS,QUITTIO-VAT-ROUNDINGandQUITTIO-CANCELLATION-VAT-ALIGNEDread the declared amounts state through the internalCalculation\DeclaredAmountsStatelike the calculator, the builder, the reader and the cancellations. The results are the same.
0.4.0
Added
- Typed view model: every node that templates receive has a class in
Quittio\Core\View\Model(PartyView,AddressView,LineView,VatRowView,TotalsView,PaymentView,DesignView,DocumentInfoView…; lists areViewList<T>, e.g.ViewList<LineView>) with its keys as typed properties, so IDEs and PHPStan know that$invoice->totals->amountDueis a?FormattedValue. The nodes areViewNodeobjects over the same data: published Blade and PHP templates, array syntax,toArray()and the mandatory field guard work unchanged (Typed nodes). The compatibility promise now names the keys and types of the view model. - The PHP rules check the XRechnung extension like the official validator:
BR-DEX-02(warning),BR-DEX-04toBR-DEX-10andBR-DEX-12toBR-DEX-14for identifier schemes withXR01toXR03, the amount due with third party payments and the third party payments themselves. In the profilexrechnung-extensionthey replaceBR-CL-10,-11,-21,-25,-26and, in UBL,BR-CO-16andQUITTIO-SUBLINES, so a violation is reported once. Profiles declare such replacements with the capabilityReplacesRules. - The Peppol profile checks all Peppol BIS Billing 3.0 rules that the data of an invoice can break, with readable messages instead of only the official validation: the format of the business process (
PEPPOL-EN16931-R007), values of only whitespace (-R008), net price against gross price and discount (-R046), the sign of BT-111 (-R055), line periods within the invoicing period (-R110,-R111), currencies and dates (-CL007,-F001), the type codes of business process 01 (-P0100,-P0101), the type codes 326 and 384 outside Germany (-P0112), VATEX codes with the wrong VAT category (-P0104to-P0111) and the format and check digits of GLN, Norwegian, Danish, Belgian, Italian, Swedish and Australian identifiers (PEPPOL-COMMON-R040to-R053). The code listsCodeList::PEPPOL_CURRENCIES,PEPPOL_INVOICE_TYPESandPEPPOL_CREDIT_NOTE_TYPESare generated from the Peppol rules withCodeListGenerator::generatePeppol()(CodeListGenerator::PEPPOL_LISTS). The rules the UBL writer always satisfies are listed in the validation docs (Peppol rules). - The PHP validation checks the VAT categories L (IGIC, Canary Islands:
BR-AF-01,-02,-05,-08,-09,-10), M (IPSI, Ceuta and Melilla:BR-AG-01,-02,-05,-08,-09,-10) and B (split payment, Italy:BR-B-01,BR-B-02). Like S, L and M allow one VAT breakdown per rate with VAT; their rate may also be 0. The warningQUITTIO-IGIC-ZERO-RATE-CIIreports IGIC with the rate 0 in CII, which the official CII Schematron rejects. - XRechnung CVD (Clean Vehicles Directive) in the German pack: the profile
xrechnung-cvd(XRechnung30Cvd, specification identifier…#compliant#urn:xeinkauf.de:kosit:xrechnung:cvd_0.9) forxrechnung-cii,xrechnung-ublandzugferd, with the rulesBR-DE-CVD-01toBR-DE-CVD-06-b: contract (BT-12) and tender or lot reference (BT-17) are required, and vehicle lines carry the category as item classification with schemeCVD(BT-158) and the item attributecva(BT-160, BT-161). The profile accepts the schemeCVDin the code list checkBR-CL-13.
Changed
- The Laravel settings of the German pack live under
quittio.countries.de(config('quittio.countries.de.base_rates'),….base_rate_file) instead of the configurationquittio-de.php artisan vendor:publish --tag=quittio-de-configpublishes them toconfig/quittio/countries/de.php, which Laravel loads asquittio.countries.de; they can also be set inconfig/quittio.phpunder'countries' => ['de' => […]].quittio:installpublishes the tagquittio-{code}-configof every installed country pack withconfig/quittio.php.GermanyServiceProviderno longer fillsquittio-dewith defaults: code that readsconfig('quittio-de.…')readsquittio.countries.de. Lower-case codes underquittio.countrieshold the settings of a pack, the convention for further packs (quittio.countries.{code}); a pack class listed under'de'is moved to a numeric key. - The calculator, the builder, the reader and the cancellations read and write the declared amounts flags together as one typed state (the internal
Calculation\DeclaredAmountsState) instead of single flags. The flags stay the stored format with the same strings, so stored data of earlier versions is read unchanged; they are now always written after the other flags in a fixed order (declared_amounts,declared_amounts_recalculated,cancellation_vat_aligned,declared_vat_in_tax_currency:,amounts_fingerprint:last), and duplicates or an unreadabledeclared_vat_in_tax_currency:value are dropped when data is calculated again. ViewModelFactorybuilds the view model with one internal factory per section (header, parties, references, lines, allowances and charges, VAT breakdown, totals, payment, notes, design, documents;View\Internal\Sections). The view model is the same as before.
Deprecated
- The configuration
quittio-de(config/quittio-de.php): move its keys toquittio.countries.deand delete the file. Until 1.0 it is still read: its keys fill those thatquittio.countries.deleaves out or keeps at the default, and the provider reports anE_USER_DEPRECATEDon every start while it is set (The old file).
Fixed
- The KoSIT validator (L5) reports a rule with the level the validator scenario gives it (
customLevel), as the validator itself does when it decides on acceptance. Before, the level of the Schematron was used, so e.g. BR-CO-16 in an XRechnung extension invoice with third party payments was reported as error although the validator accepted the document. - The official validation in CI now checks PDF/A-3b with veraPDF: the job pulls the
verapdf/cliimage (pinned by digest), and the veraPDF tests fail instead of being skipped when the image is missing in CI.tools/validate-fixtures.shalso renders every EN 16931 scenario as ZUGFeRD PDF and checks the PDFs with veraPDF; before, it generated no PDFs, and its veraPDF call would have received no files.
0.3.0
Added
- Single PHP rules can be switched off, re-rated or replaced by their code:
validation.rules.disabled,validation.rules.severityandQuittio::extend('rule', …)in Laravel,RuleOverrideswithQuittio::configureRules()in the core. Official EN 16931 and Peppol rules needallow_official; unknown codes are refused.Quittio::ruleCodes()lists the known codes. - The calculation policy decides what happens with declared amounts of other systems:
calculation.declared_amounts(keep,recalculate,reject),CalculationPolicyandDeclaredAmountsModein the core. An own calculator is configured withcalculation.calculatoror passed toQuittio::create(calculator: …);Quittio::calculator()returns it. Quittio::edit()(core and Laravel) andInvoiceBuilder::fromUsing()continue existing invoice data with the calculator of the instance; cancellations and the document calculator ofQuittioFakeuse it as well.InvoiceFactory::calculated()takes a calculator.- Tax presets whose rate depends on the date (
Quittio\Core\Tax\DatedTaxPreset) are resolved with the supply date of each line (SupplyDate: line period, VAT point date, delivery date, invoicing period, issue date).DatedValuesholds values that change on dates;TaxPresetCollection::alias()finds presets under further keys. VatPreset::Standard(de.standard) andVatPreset::Reduced(de.reduced) take the German rate of the supply date (TaxTables), e.g. 16 % and 5 % from July to December 2020. The plausibility ruleDE-VAT-RATEwarns about rates that applied neither as standard nor as reduced rate on the supply date. The small amount limit ofDE-USTG-08is that of the issue date (150.00 EUR before 2017).- Country packs register output formats, aliases and serializers (
Countries\Capabilities\ProvidesFormats, implemented byAbstractCountryPack); the German pack brings XRechnung (XRechnungFormats). Profiles declare sub lines, extension fields and third party payments with capabilities (SupportsSubLines,SupportsExtensionFields,SupportsThirdPartyPayments) instead of the core checking XRechnung keys. - Default interest for every country pack with a reference rate:
Quittio\Core\Dunning\DefaultInterest(forCountry($pack)), the contractDunning\ReferenceRatesand the capabilityProvidesReferenceRates; the German pack provides its base rates (BaseRateTableimplementsReferenceRates) and itsDefaultInterestuses the core calculation.DocumentBuilder::interestCharge()adds calculated charges with their day count and texts. - Documents can be queued in Laravel:
Quittio::document($quote)->queue()->save($disk, $path)with the jobRenderDocument, the eventsQueuedDocumentStoredandQueuedDocumentFailedandQuittioManager::queueDocument();QuittioFakerecords them (assertDocumentQueued(),queuedDocuments()). Documents are stored as versioned snapshots (DocumentData::toSnapshot(),fromSnapshot(),toArray(),toJson()…) with their own upcasters (quittio.snapshots.document_upcasters,SnapshotUpcasterChain::documentDefault());Mapping\Attributes\MapOfkeeps keyed arrays. - The German special cases are reached with the builder macro
de():->de()->smallAmount(),->de()->selfBilled(),->de()->deductAdvance($advance)… (GermanInvoice).
Changed
- Lines without VAT treatment of a German seller get the preset key
de.standardinstead ofde.standard_19, andquittio.defaults.vat = 'de.reduced_7'givesde.reduced. Applications that compare preset keys useVatPreset::fromKey($key)?->canonical(). Supplies from 2020-07-01 to 2020-12-31 get 16 % and 5 %. CiiWriter,UblWriter,CiiReaderandUblReaderkeep no state between calls: each call works with its own internal worker, so one instance can be used reentrantly. The readers no longer extend the internalXPathReaderand therefore no longer expose its constants.- The render run resolves the logo of the presentation before it builds the view model (
ViewContext::$logo,ResolvedLogo), so the view layer does no I/O;ViewModelFactorykeeps no state between calls. - The documentation website is versioned: the root shows the latest release,
/next/the development version and/X.Y/older minor versions, with a version switcher (docs/_versioning/build.mjs). Every version has a generated API reference underapi/(phpDocumentor,tools/docs/phpdoc.sh); the builder traits underBuilder\Concernsare no longer@internal, so their methods appear there. - Pull requests opened in the read-only split repositories
quittio/core,quittio/laravelandquittio/country-deare closed automatically with a pointer to the monorepo.
Deprecated
VatPreset::Standard19andVatPreset::Reduced7: useVatPreset::StandardandVatPreset::Reduced; the keysde.standard_19andde.reduced_7stay readable.Flags::SMALL_AMOUNT_LIMIT: useTaxTables::smallAmountLimitOn().- The builder macros
smallAmount(),propertyRelatedService(),newVehicle(),selfBilled(),deductAdvance()andconsumerDefaultNotice()directly on the builder: use->de()->….GermanInvoiceMacros::ADVANCE_TYPES: useGermanInvoice::ADVANCE_TYPES.FormatRegistry::XRECHNUNG_CIIandXRECHNUNG_UBL: useXRechnungFormats::CIIandUBLof the German pack, which registers the formats; the core keeps them until 1.0.
Fixed
- Peppol invoices with an electronic address scheme that Peppol does not accept, e.g.
EM(e-mail), are rejected with the new rulePEPPOL-EN16931-CL008instead of passing the PHP rules and failing the official Peppol validation. The allowed schemes are the code listCodeList::PEPPOL_EAS(peppol-eas-codes.json, from the Peppol BIS rules 3.0.20);tools/codelists/update.phpregenerates it withCodeListGenerator::generatePeppol().
0.2.0
Added
- The legal texts of the German pack (
LegalTexts) and the text of default interest come from its language files (resources/lang/{de,en}/legal.php), solang/vendor/quittio-deoverrides them in Laravel;LegalTexts::DEFAULT_INTERESTis new. quittio/country-deshipsstubs/InvoiceBuilder.stub.php, which describes its builder macros (smallAmount(),selfBilled(),deductAdvance(), …) for IDEs and PHPStan.- Tests: generated round trips through CII and UBL, fuzzing of the XML, PDF and array input, and tests for formats, mapping, serialisation, the PDF parser, mail attachments, a second boot and the escaping of the Blade templates.
- Documentation: Versioning and compatibility with the backwards compatibility promise;
ARCHITECTURE.md, decision records indocs/adr/,RELEASING.md, a reworkedCONTRIBUTING.md, an FAQ page; issue and pull request templates,CODEOWNERSand response times inSECURITY.md. Classes outside the public API (XPathReader,XmlBuilder,Pdf\Parsing,CarryOver,ConfigMapper, the command and preview classes, …) are marked@internal. - Documents (quotes, reminders, …) dispatch
DocumentRenderingandDocumentRenderedin Laravel;PendingDocumenthasoption()(core and Laravel),data(),options()and isMacroable(Laravel). Quittio::setPostProcessor()replaces the PDF/A and XML embedding for renderers that cannot embed. Profiles describe their embedded XML with the capabilityDescribesEmbeddedXml(XMP description andfx:Version) instead of the file name deciding it.- Amounts of
prepaid(),addPrepaid(),rounding(),discountPercent(),discountAmount()and of the document builder'sfee()andopeningBalance()acceptBigDecimal,intorstringlike line prices (floats are rejected).DocumentBuilder::note(),discountPercent()anddiscountAmount()take the same subject and reason codes as the invoice builder. QuittioFakedispatches the events of a real run (InvoiceRendering,InvoiceValidated,InvoiceRendered) and records queued invoices:assertQueued(),assertNothingQueued(),queued(). Its decisions follow configuration changes withoutQuittio::flush().- Events
QueuedInvoiceStored(a queued job stored the file, with disk and path) andQueuedInvoiceFailed(a queued job failed for good). Quittio\Core\Quittio::resetState()forgets the process-wide state (translator bridge, default snapshot upcasters, application default VAT, base rates of the German pack); extensions register their own state withonResetState(). The test traitInteractsWithQuittiocalls it after each test.
Changed
- README and documentation mark the Managed layer
quittio/managedas not released yet; its documentation page is a preview and is left out ofllms.txt,llms-full.txtand the MCP index. - The documentation is built and link-checked on pull requests; every PHP code block of the documentation must parse.
quittio/core,quittio/laravelandquittio/country-dedeclare their test dependencies and ship aphpunit.xml.dist, so their split repositories runcomposer teston their own.quittio/quittioregisters theQuittiofacade alias and suggestsext-intl,ext-zipandopis/json-schemalike the single packages.- Development: Composer scripts
test:fast,test:official,test:visual,test:coverage,test:mutation,docs:generate,codelists:updateandci; a weekly CI run withcomposer audit, line coverage and mutation testing; PHPStan also checkstools/*.phpand reports calls of deprecated code. - Visual baselines are written only with
QUITTIO_UPDATE_BASELINES=1; a missing baseline fails the test. The default Blade partials that print pre-escaped HTML carry a warning comment. - Queued
RenderInvoicejobs store the render options as versioned JSON (RenderOptions::toArray()/fromArray()) instead of a PHP-serialised object, so an update of Quittio no longer breaks jobs in the queue; jobs queued by 0.1.0 still run. Errors that another attempt cannot fix (validation, invalid data, configuration) fail the job at once instead of three times; other errors are retried after 10 and 60 seconds. Inside a database transaction the job is dispatched after the commit (quittio.queue.after_commit). - Every exception that Quittio throws implements
QuittioException: invalid arguments and runtime errors are nowQuittio\Core\Exceptions\InvalidArgumentException,RuntimeExceptionorLogicException, which extend the PHP classes of the same name, so existingcatchblocks keep working. - Country pack discovery keeps its result in
bootstrap/cache/quittio-countries.php(and per process) until Composer changes the installed packages, instead of readingvendor/composer/installed.jsonwhenever the core is built. - The Laravel configuration no longer defaults to Germany:
defaults.country(QUITTIO_COUNTRY) andseller.address.countryarenull, which means the only installed country pack (Germany withquittio/quittio), so existing applications keep their behaviour. With several packs, setdefaults.countryor the seller's country.Quittio::registerCountry()makes the next call build the core again. - The Laravel manager builds a new core when the
quittioconfiguration changes, soQuittio::flush()is no longer needed afterconfig()->set(). Under Octane it reads the configuration of the current request instead of the application that created the singleton; so does the default VAT of lines without VAT.
Deprecated
- Environment variables are named after their configuration keys:
QUITTIO_DEFAULT_COUNTRY,QUITTIO_VALIDATION_MODE,QUITTIO_MANDATORY_FIELD_GUARD,QUITTIO_STORAGE_DISKandQUITTIO_PREVIEW_ENABLED. The names of 0.1.0 (QUITTIO_COUNTRY,QUITTIO_VALIDATION,QUITTIO_GUARD,QUITTIO_DISK,QUITTIO_PREVIEW) are still read when the new one is not set, until 1.0. - The methods of
Quittio\Core\Quittiothat change the instance are named after what they do:addSerializer(),addValidator(),clearValidators(),setRenderer(),setTemplateEngine(),setPaymentQrGenerator()andsetFormatResolver(). Thewith…()names, which elsewhere in Quittio return a changed copy, remain as deprecated aliases until 1.0.
Security
quittio:schemas:downloadchecks the KoSIT validator, the XRechnung configuration, the Peppol rules and SchXslt against pinned SHA-256 checksums before it unpacks or runs them; so do the download scripts intools/.- Decimal strings are only accepted in plain notation (
Decimal::of(), builders,InvoiceData::fromArray()/fromJson(), snapshots): an exponent such as1e999999999was expanded to a billion digits and exhausted the memory. Other notations throw aNumberFormatException(aHydrationExceptionfor array and JSON input). - The preview route refuses requests outside the
localenvironment unless the gateviewQuittioPreviewallows them; before,QUITTIO_PREVIEW=truemade it public behind thewebmiddleware only. Requests are limited to 30 per minute (quittio.preview.throttle).QuittioServiceProvider::registerPreviewRoutes()is no longer public.
Fixed
- Default interest of payment reminders was described in English for German locales with a region such as
de_AT. InvoiceData::fromJson()threw PHP'sJsonExceptionfor JSON that cannot be parsed (empty, too deep, invalid UTF-8); it now throws aHydrationException.JsonSchemaValidator::validate()reports such input as a finding.DE-USTG-08names the limit ofFlags::SMALL_AMOUNT_LIMIT(250.00 EUR) instead of a second, hard-coded value. The English fallback messages of four UStG rules matched their translations no longer; a test now compares every rule's fallback withlang/en/rules.phpand requires a German message.- The field reference documents the UBL credit note path of BT-129 and BT-130 (
cbc:CreditedQuantity) and no longer shows pseudo-XPath for BT-110 and BT-111. A test now evaluates every documented CII and UBL path against the XML of the sample invoices and of the official XRechnung test suite. - A custom
TotalsCalculatorwas bypassed by the VAT alignment of partial cancellations;Cancellation::of()andDeclaredAmounts::needed()accept the calculator, andQuittio::cancellation()passes its own. - Texts with control characters (e.g. a vertical tab copied from a spreadsheet cell) or invalid UTF-8 produced XML that was not well-formed and was rejected by every receiver. The new rule
QUITTIO-INVALID-CHARACTERSnames the field, and the CII and UBL writers refuse such data with anInvalidInvoiceDataexception. - A UBL credit note without payment instructions (BG-16) left out its due date (BT-9) silently: the UBL binding transmits it only in
cac:PaymentMeans. The new warningQUITTIO-CREDIT-NOTE-DUE-DATEpoints this out; the field reference names the credit note path. - A percentage document allowance or charge without base amount whose VAT category and rate matched no invoice line became 0.00 silently (data from JSON, readers or
with()); the calculator now throws aCalculationException. Drafts without any lines still calculate with a zero base. - Reading an invoice and the check for declared amounts no longer turn errors such as a
TypeErrorof a customTotalsCalculatorinto the warningQUITTIO-READER-RECALCULATION; only exceptions are reported. - The post-processing of PDFs from renderers that cannot embed (PDF/A and XML) uses the configured
temp_dirinstead of the system temporary directory. quittio:validate: an unknown--profileprinted a stack trace; files that cannot be checked (missing, not an e-invoice, unknown profile) now exit with2instead of1, so scripts can tell them from invalid files; with--jsonsuch errors are printed as{"valid": false, "error": …}and the result namesexpectedProfileandprofileMismatch.- Failed writes are reported w...
v0.1.0
First release.
Added
- Monorepo
quittio/quittio(the complete package) with the read-only splitsquittio/core,quittio/laravelandquittio/country-de. - EN 16931 data model with mapping attributes (BT, cardinality, profile level, CII and UBL paths), builders, JSON conversion, generated JSON schema and field reference.
- Calculation according to EN 16931 including document allowances split by VAT rate, gross prices, foreign currency and third party payments.
- Profiles MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED, Peppol BIS 3; XRechnung 3.0 and the XRechnung extension in the country pack Germany.
- Validation levels L1 to L7: PHP rules (EN 16931, Peppol, BR-DE, UStG), XSD, KoSIT validator (daemon and CLI), veraPDF (CLI and Docker), plausibility checks.
- CII and UBL writers and readers with protection against XXE, entity expansion and oversized input; PDF reader for embedded invoices with decompression limits.
- ZUGFeRD/Factur-X PDF/A-3b with mPDF, XMP metadata and associated files; post processing of foreign PDFs; EPC GiroCode.
- View model with formatted and raw values, mandatory field guard, plain PHP and Blade default templates with partials, DIN 5008 letter layouts.
- Format resolver, entry point
Quittio\Core\Quittioand Laravel facade with several formats per call, storage, downloads, mail attachments, queue, events, preview route and Artisan commands. - Quotes, order confirmations, delivery notes, proforma invoices, payment reminders and account statements.
- Country pack Germany: tax presets, UStG rules, format rules, legal texts, BMF error classes, numbering, retention, dunning law with base rate table, correction policy, builder helpers.
- Testing helpers: fake, assertions, invoice factory, sample scenarios, visual regression (identical baselines on macOS and the Linux CI image).
quittio:schemas:downloadcompiles the Peppol BIS Billing 3.0 rules; KoSIT configuration accepts several scenario sets.- Base rates up to 1 July 2026; small business invoices according to section 34a UStDV.
- Full cancellations (
Quittio::cancellation()) with configurable sign strategy: type code 381 with positive amounts (default) or 380 with negative amounts (corrections.cancellation_strategy). Quittio::prepare()/PendingInvoice::prepare(): calculation, format and profile choice and validation without rendering.InvoiceBuilder::updateLine(),removeLine()andInvoiceLine::withNetPrice().- Complete reference documentation: new pages Building invoices, Laravel integration and Configuration; every builder method, config key (both packages), command, event, exception, tax preset and rule is documented, the Managed layer page covers models, numbering, archive, immutability, delivery, audit log, authorisation and retention in full.
- Documentation website built from
docs/with Jekyll and Just the Docs (navigation, search), deployed to Cloudflare by.github/workflows/docs.yml; the Markdown files stay the only source. - Country pack Germany: tax preset
VatPreset::TriangularTransactionfor intra-community triangular transactions (section 25b UStG, wording of section 14a (7) UStG). - Country pack Germany: rule
DE-USTG-11requires the VAT identifiers of seller and buyer for reverse charge services in another member state and for triangular transactions (section 14a (1) and (7) UStG); the tax number or a legal registration identifier is not sufficient there. - Country pack Germany:
->deductAdvance($advanceInvoice)for final invoices (section 14 (5) sentence 2 UStG): references the paid advance invoice, adds it to BT-113 and itemises its net amounts and VAT per rate in a note.InvoiceBuilder::addPrepaid()adds to BT-113. quittio.seller.small_businessnow takes effect:Seller::apply()marks the invoice (Meta::FLAG_SMALL_BUSINESS) and the German pack rejects VAT on it (DE-USTG-09).- Official KoSIT test for every German tax preset in XRechnung CII and UBL; the XRechnung golden-file tests run in the official CI job; CI validates every package
composer.json. - Laravel:
format_resolver.peppol_lookup(invokable class or callable receiving theBuyerParty) for the automatic Peppol choice;Quittio::coreWithLevels();RenderedDocument::hasPdf();Quittio::fake()->documents(). - Core:
LineBuilder::showGrossPrice();InvoiceBuilder::payee()accepts aPayeeParty, identifier schemes or aPartyBuildercallable;Quittio::cancellation()takes an optional$title; country pack readers (CountryPack::readers()) are used byread()andvalidateFile(). - Country pack Germany: info
DE-LEITWEG-ID-SUSPECTfor buyer references shaped like a Leitweg-ID with wrong check digits; warningDE-USTG-13whennewVehicle()is used without a line of category K. - Country pack Germany: the base rate (§ 247 BGB) can be updated without a release:
quittio-de.base_ratesin the new configurationconfig/quittio-de.php,php artisan quittio:base-rate:updatefetching it from the statistics API of the Deutsche Bundesbank (BundesbankBaseRates), orBaseRates::register();DefaultInterestuses these rates by default. The pack has a Laravel service provider of its own (GermanyServiceProvider, discovered automatically). - Country pack Germany: tax numbers (BT-32) of German sellers are checked for format and check digit according to the ELSTER procedures of every federal state (warning
DE-TAX-NUMBER);TaxNumberparses the ELSTER, federal and state notations. - Country pack Germany: rule
DE-USTG-14rejects intra-community supplies (category K) of small businesses except for new vehicles (section 19 (1) sentence 2 and (6) sentence 1 UStG); warningDE-USTG-15for invoices on intra-community supplies and on services in another member state issued after the 15th of the month following the supply (section 14a (1) sentence 2 and (3) sentence 1 UStG);UstgRules::issuingDeadline(). - Documentation for AI assistants:
llms.txt,llms-full.txtand every page as Markdown on the website, a read-only MCP server athttps://docs.quittio.dev/mcp(toolssearch_docs,get_page,list_pages) and Laravel Boost guidelines and the skillquittio-developmentinquittio/quittioandquittio/laravel. - Core: shorter invoice code. Lines can be given as name with named values (
->line('Consulting', netPrice: '120.00', quantity: 4, unit: UnitCode::Hour), alsovat,descriptionandpriceInclVat; the same inLine::make(),subLine()and the document builder), asLine::make('Consulting', …)->…or as arrays whose keys are theLineBuildermethods (LineBuilder::ARRAY_KEYS,fill(),Line::fromArray()), parties asParty::make('Kunde AG')->…or as application models:->buyer($customer)for models implementingProvidesParty.subLine()and the document builder accept the same forms. - Core: default VAT for lines without
vat():InvoiceBuilder::defaultVat()andDocumentBuilder::defaultVat()per invoice,InvoiceBuilder::useDefaultVat()for the application,CountryDefaultVat(configured preset, otherwise the standard preset of the seller's country pack, for small business invoices its small business preset),CountryDefaults::$vatPresetand$smallBusinessVatPreset(Germany:de.standard_19,de.small_business),CountryRegistry::taxPreset(). - Core:
InvoiceBuilder::applyDefaults()remembers the defaults of a seller;seller()with another seller removes them again, except values the caller set.Quittio::invoice()and the document methods use it, so another seller gets neither the bank account nor the small business flag (nor, with quittio/managed, the payment terms, design, locale, time zone and currency) of the configured seller. validation.locale: the language of validation messages, independent of the invoice language. In Laravel the default is the language of the application (app()->getLocale()),'invoice'restores the language of the invoice; without Laravel the default stays the invoice language,'app'uses the translator's locale.ValidationContext::messageLocale().- Laravel:
Quittio::invoice()->…->check()validates without rendering. - Core: fewer callbacks.
dueInDays(),cashDiscount()andlateFee()on the invoice and document builder replacepaymentTerms(fn ($t) => …);presentation()takes an array of changes;updateLine('2', netPrice: '80.00', quantity: 3)takes named values;payee()takes aPartyBuilder. The callbacks keep working. - Laravel:
QUITTIO_SELLER_SMALL_BUSINESSforquittio.seller.small_business. - Laravel:
config/quittio.phpexplains every key;pdf.colors.secondaryis listed (defaultnull, as before). - PDF: carry-over across pages. When the line table continues on the next page, the page ends with "Fortsetzung auf Seite 2 · Übertrag" and the sum of the lines so far, the next page starts with "Übertrag von Seite 1" (English "Continued on page 2 · Carried forward", "Brought forward from page 1"). The mPDF renderer lays a document out again only when the lines span pages; own templates enable it with markers (templates), view model
lineGroups[].lines[].carryOver. Following pages get a header with logo or name, title and number, recipient, customer number and date. - Fonts: Geist (SIL Open Font License) is bundled and the default font, with the family
geistmedium(Medium, SemiBold) for medium weights; Inter stays bundled (pdf.font = 'inter', also for Greek). - View model:
design.fontMedium,design.continuationMarginTop,design.logoMaxHeight,design.footerHasTaxIds,totals.dueEqualsGross; labelspayment,transfer_until,remittance_short,vat_row_single,your_vat_id,your_tax_number,tax_number_short,carry_over,carry_over_from,continued_on. - Documentation for first-time users: new pages E-invoicing basics (EN 16931, CII and UBL, formats, profiles, rule codes, which format you need, German terms) and Troubleshooting (co...