InvoiceTaxManager
in package
Applies VAT of a manually issued invoice from the unified tax engine.
Counterpart of OrderTaxManager for Invoicer: the only
place where a manual invoice (admin form, invoice template, scheduler,
InvoiceComposer and Company through it) learns
anything about VAT. TaxManager decides the rate, treatment code, clause and summary
report code; this class only maps them to invoice lines and the header snapshot
(invoices.tax_*). Consumers then just read the snapshot.
An invoice created from a payment does not belong here: it copies the snapshot from the payment (InvoiceGenerator::applyTaxTreatment()), so the invoice shows what the customer saw at payment. Call this manager only where the invoice originates and the treatment is still undecided.
Contract:
- Nothing is saved. Only the passed (unsaved or just edited) entity changes; totals are
recomputed by InvoiceCalculator in
beforeSave(). - Backward compatible. An invoice with no line that has a supply kind, and no manual override,
does not call the engine:
apply()returnsfalseand the rates stay as the caller sent them. The same happens when the engine returns no usable answer (missing seller status for the date, rate or supply kind). - A manual override wins. InvoiceTaxOverride replaces the code and rate of all lines
with a supply kind, the invoice gets the
MANUAL_OVERRIDEflag and the reason intax_override_reason. TheDOMESTIC_REVERSE_CHARGEcode arises only this way. - The clause is written in the invoice language plus Czech (DocumentLocale::taxNote()), not in the page language.
IMPORTANT: TaxManager reads the code lists with raw DBAL, so it is safe to call in the middle
of building an invoice object graph; a find() here would run UoW::clear() and break the graph.
Table of Contents
Constants
- MONEY_SCALE : int = 3
- Scale of the Invoicer money columns (`decimal(10,3)`).
Properties
- $languageManager : LanguageManager
- $supplyKindManager : SupplyKindManager
- $taxManager : TaxManager
- $translator : Translator
Methods
- __construct() : mixed
- apply() : bool
- Determines VAT of the whole invoice and writes it to the entity: rate and supply kind on the lines, header `use_vat` (derived from `chargeVat`) and the tax snapshot.
- displayRate() : string
- Returns the rate for display and for prefilling a line in the admin: an anonymous consumer in the country of the invoice's payment region. An unknown supply kind or a seller without status gives `'0.00'` (never a silent default).
- resolve() : TaxResolution|null
- Returns the treatment of the invoice on the tax point date; `null` when the engine is not called (no line has a supply kind) or gave no usable answer. Callers may request the resolution on its own (admin preview).
- applyHeader() : void
- Writes the header snapshot: treatment code, date and country of the rate, clause, VAT id verification, flags and summary report code. `use_vat` is only derived from `chargeVat`; the calculator and the PDF read it.
- applyLines() : void
- Writes the rates and the `use_vat` flag of the lines. A line without a supply kind (rounding, a hand-written item of an old invoice) is unknown to the engine: it keeps its rate, but the header silences it anyway when no tax is charged (the calculator uses `useVat && line.useVat`).
- applyOverrideOnly() : bool
- Applies a manual override when the engine gave no usable answer (invoice without supply kinds, seller without status): the admin said explicitly how to tax the invoice, so the choice holds anyway. The snapshot then carries only what is certain (code, rate, date and reason); the country of the rate stays empty.
- buildContext() : TaxContext
- Builds the customer context for the engine: ISO country from the invoice snapshot (empty means the engine derives it from the payment region and sets `COUNTRY_ASSUMED`), VAT id, company id and business nature.
- buildLines() : array<int, TaxLine>
- Builds the engine lines: invoice items that have a supply kind, base after discount times quantity. The order matches the order of such items on the invoice (`applyLines()` walks the same loop), so results map back by index.
- isUsable() : bool
- itemBase() : string
- Line base after discount times quantity; the same values `InvoiceCalculator` uses.
- money() : numeric-string
- Returns a decimal column value for bcmath; non-numeric or empty counts as zero.
- normalizeCountryCode() : string|null
- Normalizes to an uppercase ISO2 code; empty or invalid gives `null` (the engine derives the country itself).
- normalizeDate() : DateTimeImmutable
- rateOf() : string
- Rate of the treatment for the snapshot: `0.00` when no tax is charged (or the engine returned no line).
- summaryReportCode() : string|null
- Returns the summary report code for a manual override: `3` for services with reverse charge, `0` for goods supplied to the EU. Other codes are not reported; domestic reverse charge goes to a separate ledger, not to the summary report.
- translateNote() : string|null
- Translates the VAT clause into the invoice language plus Czech; `null` when the code has no clause (domestic).
Constants
MONEY_SCALE
Scale of the Invoicer money columns (`decimal(10,3)`).
private
int
MONEY_SCALE
= 3
Properties
$languageManager read-only
private
LanguageManager
$languageManager
$supplyKindManager read-only
private
SupplyKindManager
$supplyKindManager
$taxManager read-only
private
TaxManager
$taxManager
$translator read-only
private
Translator
$translator
Methods
__construct()
public
__construct(TaxManager $taxManager, SupplyKindManager $supplyKindManager, LanguageManager $languageManager, Translator $translator) : mixed
Parameters
- $taxManager : TaxManager
- $supplyKindManager : SupplyKindManager
- $languageManager : LanguageManager
- $translator : Translator
apply()
Determines VAT of the whole invoice and writes it to the entity: rate and supply kind on the lines, header `use_vat` (derived from `chargeVat`) and the tax snapshot.
public
apply(Invoice $invoice, int $sellerId[, DateTimeInterface|null $taxDate = null ][, InvoiceTaxOverride|null $override = null ][, string|null $supplyPlaceCountryCode = null ][, bool|null $customerIsBusiness = null ]) : bool
Parameters
- $invoice : Invoice
- $sellerId : int
- $taxDate : DateTimeInterface|null = null
- $override : InvoiceTaxOverride|null = null
- $supplyPlaceCountryCode : string|null = null
-
ISO2 country of the property; read only for the
immovable_servicenature - $customerIsBusiness : bool|null = null
-
nullderives it from the customer's company and VAT ids on the invoice
Return values
bool —true when the snapshot was written; false when the engine was not called
or gave no usable answer and the invoice stayed unchanged
displayRate()
Returns the rate for display and for prefilling a line in the admin: an anonymous consumer in the country of the invoice's payment region. An unknown supply kind or a seller without status gives `'0.00'` (never a silent default).
public
displayRate(int $sellerId, int|null $paymentRegionId, int|null $supplyKindId[, DateTimeInterface|null $at = null ]) : string
Parameters
- $sellerId : int
- $paymentRegionId : int|null
- $supplyKindId : int|null
- $at : DateTimeInterface|null = null
Return values
stringresolve()
Returns the treatment of the invoice on the tax point date; `null` when the engine is not called (no line has a supply kind) or gave no usable answer. Callers may request the resolution on its own (admin preview).
public
resolve(Invoice $invoice, int $sellerId[, DateTimeInterface|null $taxDate = null ][, string|null $supplyPlaceCountryCode = null ][, bool|null $customerIsBusiness = null ]) : TaxResolution|null
Parameters
- $invoice : Invoice
- $sellerId : int
- $taxDate : DateTimeInterface|null = null
- $supplyPlaceCountryCode : string|null = null
- $customerIsBusiness : bool|null = null
Return values
TaxResolution|nullapplyHeader()
Writes the header snapshot: treatment code, date and country of the rate, clause, VAT id verification, flags and summary report code. `use_vat` is only derived from `chargeVat`; the calculator and the PDF read it.
private
applyHeader(Invoice $invoice, TaxResolution $resolution, int $sellerId, DateTimeImmutable $date, InvoiceTaxOverride|null $override) : void
Parameters
- $invoice : Invoice
- $resolution : TaxResolution
- $sellerId : int
- $date : DateTimeImmutable
- $override : InvoiceTaxOverride|null
applyLines()
Writes the rates and the `use_vat` flag of the lines. A line without a supply kind (rounding, a hand-written item of an old invoice) is unknown to the engine: it keeps its rate, but the header silences it anyway when no tax is charged (the calculator uses `useVat && line.useVat`).
private
applyLines(Invoice $invoice, TaxResolution $resolution, InvoiceTaxOverride|null $override) : void
Parameters
- $invoice : Invoice
- $resolution : TaxResolution
- $override : InvoiceTaxOverride|null
applyOverrideOnly()
Applies a manual override when the engine gave no usable answer (invoice without supply kinds, seller without status): the admin said explicitly how to tax the invoice, so the choice holds anyway. The snapshot then carries only what is certain (code, rate, date and reason); the country of the rate stays empty.
private
applyOverrideOnly(Invoice $invoice, int $sellerId, DateTimeImmutable $date, InvoiceTaxOverride $override) : bool
Parameters
- $invoice : Invoice
- $sellerId : int
- $date : DateTimeImmutable
- $override : InvoiceTaxOverride
Return values
boolbuildContext()
Builds the customer context for the engine: ISO country from the invoice snapshot (empty means the engine derives it from the payment region and sets `COUNTRY_ASSUMED`), VAT id, company id and business nature.
private
buildContext(Invoice $invoice, int $sellerId, DateTimeImmutable $date, string|null $supplyPlaceCountryCode, bool|null $customerIsBusiness) : TaxContext
VatIdPolicy::CacheOnly is deliberate: a manual invoice is issued in the admin, where VAT
id verification is a separate button. Saving the invoice must not depend on VIES availability.
Parameters
- $invoice : Invoice
- $sellerId : int
- $date : DateTimeImmutable
- $supplyPlaceCountryCode : string|null
- $customerIsBusiness : bool|null
Return values
TaxContextbuildLines()
Builds the engine lines: invoice items that have a supply kind, base after discount times quantity. The order matches the order of such items on the invoice (`applyLines()` walks the same loop), so results map back by index.
private
buildLines(Invoice $invoice) : array<int, TaxLine>
Parameters
- $invoice : Invoice
Return values
array<int, TaxLine>isUsable()
private
isUsable(TaxResolution $resolution) : bool
Parameters
- $resolution : TaxResolution
Return values
boolitemBase()
Line base after discount times quantity; the same values `InvoiceCalculator` uses.
private
itemBase(InvoiceItem $item) : string
Parameters
- $item : InvoiceItem
Return values
stringmoney()
Returns a decimal column value for bcmath; non-numeric or empty counts as zero.
private
money(string $value) : numeric-string
Parameters
- $value : string
Return values
numeric-stringnormalizeCountryCode()
Normalizes to an uppercase ISO2 code; empty or invalid gives `null` (the engine derives the country itself).
private
normalizeCountryCode(string|null $countryCode) : string|null
Parameters
- $countryCode : string|null
Return values
string|nullnormalizeDate()
private
normalizeDate(DateTimeInterface|null $date) : DateTimeImmutable
Parameters
- $date : DateTimeInterface|null
Return values
DateTimeImmutablerateOf()
Rate of the treatment for the snapshot: `0.00` when no tax is charged (or the engine returned no line).
private
rateOf(TaxTreatment|null $treatment) : string
Parameters
- $treatment : TaxTreatment|null
Return values
stringsummaryReportCode()
Returns the summary report code for a manual override: `3` for services with reverse charge, `0` for goods supplied to the EU. Other codes are not reported; domestic reverse charge goes to a separate ledger, not to the summary report.
private
summaryReportCode(TaxTreatmentCode $code) : string|null
Parameters
- $code : TaxTreatmentCode
Return values
string|nulltranslateNote()
Translates the VAT clause into the invoice language plus Czech; `null` when the code has no clause (domestic).
private
translateNote(Invoice $invoice, string|null $noteKey) : string|null
Parameters
- $invoice : Invoice
- $noteKey : string|null