CoolCMS API

InvoiceFormTrait uses trait:short, trait:short, trait:short

Edit/add form of an invoice, built on the skeleton of {@see InvoiceTemplateFormTrait}.

Adds the billing line (the number is always generated, never typed), dates, payment picker, status and the e-mail block (defaults by language). Line amounts are computed client-side by invoice-calc.js and overwritten authoritatively by InvoiceCalculator; the Api regenerates the PDF and QR after saving. "Create invoice from template" prefills from an InvoiceTemplate (itId).

Table of Contents

Constants

PRICE_PATTERN  : string = '\s*-?\d{1,7}([.,]\d{1,3})?\s*'
Price mask for `decimal(10,3)`: up to 7 integer digits, 0-3 decimals.
QUANTITY_PATTERN  : string = '\s*-?\d{1,6}([.,]\d{1,2})?\s*'
Quantity mask for `decimal(8,2)`: up to 6 integer digits, 0-2 decimals, comma or dot, surrounding spaces tolerated.
SUPPLY_KIND_SETTING_MODULE_INVOICER  : mixed = 7
Module "invoicer" in the settings table (`cms_system_settings.module`); holds the portal `sellerId`.

Properties

$supplyKindFieldsManager  : SupplyKindManager
$supplyKindFieldsSellerManager  : SellerManager
$supplyKindFieldsVatRateManager  : VatRateManager
$invoiceSupplyKindDefaultId  : int|null
Default supply kind of a new line; `null` when the code list offers none.
$invoiceSupplyKindOptions  : array<int, string>|null
Options of the supply kind select on a line (loaded once per request).

Methods

getInvoiceSupplyKindProto()  : array{options: array, defaultId: int|null}
Returns the options and the default kind for the client prototype of a new line (`lineProto`).
addSupplyKindSelect()  : SelectBox
Adds the supply kind select. The options are database names, so the select translator is off and the prompt is translated here. IMPORTANT: the label must stay a translation key (`BaseControl::getLabel()` takes the translator from the form); a translated label would be translated twice.
buildSupplyKindRegionVatMaps()  : array<int, array<int, string>>
Returns the rate map for the helper field "price with VAT": `regionId => supplyKindId => percent` (valid today).
buildSupplyKindVatMapAttributes()  : array<int, array{json: string, map: array}>
Returns the same as {@see buildSupplyKindRegionVatMaps()} plus JSON for the `data-price-vat-map` attribute of the region matrix (keys as strings, because JS reads `map[select.value]`).
createComponentEditForm()  : Form
getSupplyKindOptions()  : array<int, string>
Returns active supply kinds as `id => name` in the admin language.
resolveSupplyKindDefaultId()  : int|null
supplyKindIdValue()  : int|null
Converts the supply kind select value to an id, or null for the empty prompt.
templateItemDescriptions()  : array<string, array<int, string|null>>
Returns descriptions of the items of invoice template `$templateId` (key {@see templateItemKey()} to descriptions in item order).
templateItemKey()  : string
Key of a template item to form row pair: code + name.
addCustomerBlock()  : void
addDeliveryBlock()  : void
addEmailBlock()  : void
addGiftsMultiplier()  : void
Same gate as {@see addItemsMultiplier()}, limit for gifts.
addInvoiceHeaderFields()  : void
addInvoiceTaxFields()  : void
Panel "VAT": manual override of the tax treatment.
addItemsMultiplier()  : void
The row count limit is read from the raw HTTP data (`onAnchor`, see {@see MultiplierLimits::guard()}), with `$maxCopies` as the second line of defense against materialization.
addLineControls()  : void
addNullableSelect()  : SelectBox
addPaymentMethodBlock()  : void
addPickerRefreshButtons()  : void
Hidden `*Refresh` submit buttons: after a pick, `picker.js` clicks them (naja), the server fills the seller/customer snapshot from the picked entity (nested contacts/accounts too) and redraws the block snippet.
addSellerBlock()  : void
addShipperBlock()  : void
addVouchersMultiplier()  : void
Same gate as {@see addItemsMultiplier()}, limit for vouchers.
applyInvoiceTax()  : bool
Applies the invoice VAT from the engine plus the manual override.
applyLine()  : void
applyNewInvoiceDefaults()  : void
New invoice (no template): dates today and today + `duePeriod`, `IN_INVOICE` e-mail defaults.
buildGifts()  : ArrayCollection<int, InvoiceGift>
buildItems()  : ArrayCollection<int, InvoiceItem>
buildPaymentMethods()  : ArrayCollection<int, InvoicePaymentMethod>
buildShippers()  : ArrayCollection<int, InvoiceShipper>
buildVouchers()  : ArrayCollection<int, InvoiceVoucher>
countryOptions()  : array<string, string>
Returns ISO country options for the customer select: active countries, CZ and SK first.
dateOrNull()  : DateTime|null
`<input type=date>`: strict parser (see `StrictDateInput`), not `new DateTime()`.
dateOrToday()  : DateTime
Parses a date or falls back to today for a truly empty field.
defaultUnit()  : string
fillMappedFields()  : void
Force-sets form fields from prefill data through a fieldMap (data key to field name; an empty map means the key is the field name, as for the seller). Only existing fields; a checkbox becomes bool.
firstItemVat()  : string|null
Returns the rate of the first line of the invoice, used to prefill the manual override field.
firstLineDefaults()  : array<string, mixed>
flashInvoiceEmailAfterSave()  : void
Outcome of the e-mail sent by the Api right after the save (`saveInfo.invoiceEmail`); nothing when no e-mail was due (not checked, already sent).
giftDefaults()  : array<int, array<string, mixed>>
isInvoiceEdit()  : bool
Whether a saved invoice (has id) is edited; add and template prefill (transient copy) return false.
isInvoiceLocked()  : bool
Whether the invoice is locked: its own state `lockedDatetime`, set only by the Api (e-mail dispatch, credit note or cancellation of the invoice, explicit lock).
lineDefaults()  : array<int, array<string, mixed>>
lineRow()  : array<string, mixed>
loadInvoiceSupplyKinds()  : void
Loads the line supply kind options and the default kind (mirror of {@see InvoiceTemplateFormTrait::loadInvoiceTemplateSupplyKinds()}).
pickedId()  : int
Reads the picked id safely from the hidden picker field (after submit).
pickerRefreshButton()  : void
resolveSupplyKindSellerCountryCode()  : string|null
Returns the portal seller country (`7/invoice.default.sellerId`) as a fallback for a region without a country; null when the seller is not found.
rows()  : iterable<string|int, array<string, mixed>|ArrayHash<string|int, mixed>>
saveInvoiceForm()  : bool
saveLockedInvoice()  : bool
Saves a locked invoice: only the allowed fields are written by a direct update (no recalculation, no new PDF). Other form values are dropped and the admin gets an explanation.
setInvoiceFormDefaults()  : void
snapshotLineIdentity()  : InvoicePaymentMethod}
Snapshot of the lines before the full replace, indexed by id, so `build*()` can pull in columns the form does not render (line identity).
statusOptions()  : array<string, string>
taxTreatmentOptions()  : array<string, NotTranslate>
Returns treatment code options for the manual override: translated names from the form domain (`form.fields.taxOverrideCode.codes.<CODE>`), ordered as the enum.
trInvoice()  : string
Translates a key in the form domain to a final string (for fields with `setTranslator(null)`).
voucherDefaults()  : array<int, array<string, mixed>>

Constants

PRICE_PATTERN

Price mask for `decimal(10,3)`: up to 7 integer digits, 0-3 decimals.

private string PRICE_PATTERN = '\s*-?\d{1,7}([.,]\d{1,3})?\s*'

Without a rule "1 000" was silently stored as 0. The minus is part of the mask because credit note lines carry a negative price and the admin must be able to save them.

QUANTITY_PATTERN

Quantity mask for `decimal(8,2)`: up to 6 integer digits, 0-2 decimals, comma or dot, surrounding spaces tolerated.

private string QUANTITY_PATTERN = '\s*-?\d{1,6}([.,]\d{1,2})?\s*'

A thousands space inside the number is an error on purpose: without a rule "1 000" passes Decimal::fromInput() as null and is silently stored as quantity 1, and a loud message is the only thing that tells the user.

IMPORTANT: the mask accepts a leading minus, like PRICE_PATTERN. The column is signed and negative quantities arrive from legacy documents, imports and the Api; without the minus the admin could not save such an invoice again, even unchanged. Credit notes do not create negative quantities (they mirror the positive quantity and negate the price in OrderInvoiceGenerator::mirrorLine()).

SUPPLY_KIND_SETTING_MODULE_INVOICER

Module "invoicer" in the settings table (`cms_system_settings.module`); holds the portal `sellerId`.

private mixed SUPPLY_KIND_SETTING_MODULE_INVOICER = 7

Properties

$invoiceSupplyKindDefaultId

Default supply kind of a new line; `null` when the code list offers none.

private int|null $invoiceSupplyKindDefaultId = null

$invoiceSupplyKindOptions

Options of the supply kind select on a line (loaded once per request).

private array<int, string>|null $invoiceSupplyKindOptions = null

Methods

getInvoiceSupplyKindProto()

Returns the options and the default kind for the client prototype of a new line (`lineProto`).

public getInvoiceSupplyKindProto() : array{options: array, defaultId: int|null}

IMPORTANT: do not pass them through $this->template->... from the form factory; the form is created lazily at {form editForm}, so the template value would not exist yet.

Return values
array{options: array, defaultId: int|null}

addSupplyKindSelect()

Adds the supply kind select. The options are database names, so the select translator is off and the prompt is translated here. IMPORTANT: the label must stay a translation key (`BaseControl::getLabel()` takes the translator from the form); a translated label would be translated twice.

protected addSupplyKindSelect(Form $form, string $name, string $labelKey, string|null $promptKey[, string|null $descriptionKey = null ][, int|null $currentId = null ]) : SelectBox
Parameters
$form : Form
$name : string
$labelKey : string
$promptKey : string|null

null = no prompt (the field is required, the first or current kind is preselected)

$descriptionKey : string|null = null
$currentId : int|null = null
Return values
SelectBox

buildSupplyKindRegionVatMaps()

Returns the rate map for the helper field "price with VAT": `regionId => supplyKindId => percent` (valid today).

protected buildSupplyKindRegionVatMaps(iterable<string|int, PaymentRegion> $regions) : array<int, array<int, string>>
Parameters
$regions : iterable<string|int, PaymentRegion>
Return values
array<int, array<int, string>>

buildSupplyKindVatMapAttributes()

Returns the same as {@see buildSupplyKindRegionVatMaps()} plus JSON for the `data-price-vat-map` attribute of the region matrix (keys as strings, because JS reads `map[select.value]`).

protected buildSupplyKindVatMapAttributes(iterable<string|int, PaymentRegion> $regions) : array<int, array{json: string, map: array}>

For the template: regionId => {json, map}.

Parameters
$regions : iterable<string|int, PaymentRegion>
Return values
array<int, array{json: string, map: array}>

getSupplyKindOptions()

Returns active supply kinds as `id => name` in the admin language.

protected getSupplyKindOptions([int|null $keepId = null ]) : array<int, string>
Parameters
$keepId : int|null = null
Return values
array<int, string>

resolveSupplyKindDefaultId()

protected resolveSupplyKindDefaultId(SupplyKindManager $manager, array<int, mixed> $options[, string|null $preferredKey = null ]) : int|null
Parameters
$manager : SupplyKindManager
$options : array<int, mixed>

select options (SupplyKindManager::findSelectOptions())

$preferredKey : string|null = null
Return values
int|null

supplyKindIdValue()

Converts the supply kind select value to an id, or null for the empty prompt.

protected supplyKindIdValue(mixed $value) : int|null
Parameters
$value : mixed
Return values
int|null

templateItemDescriptions()

Returns descriptions of the items of invoice template `$templateId` (key {@see templateItemKey()} to descriptions in item order).

protected templateItemDescriptions(int $templateId) : array<string, array<int, string|null>>

The default returns nothing; InvoicePresenter overrides it because it loads templates. Invoicing of jobs (Company WorkList) does not create invoices from templates.

Parameters
$templateId : int
Return values
array<string, array<int, string|null>>

templateItemKey()

Key of a template item to form row pair: code + name.

protected templateItemKey(mixed $code, mixed $name) : string
Parameters
$code : mixed
$name : mixed
Return values
string

addGiftsMultiplier()

Same gate as {@see addItemsMultiplier()}, limit for gifts.

private addGiftsMultiplier(Form $form) : void
Parameters
$form : Form

addInvoiceTaxFields()

Panel "VAT": manual override of the tax treatment.

private addInvoiceTaxFields(Form $form) : void

The treatment code, rate and clause are decided by InvoiceTaxManager from the supply kinds of the lines and the customer data; the admin enters only through this override, and only with a reason. Without a reason the invoice is not saved: the rule hangs on the taxOverrideReason field (addConditionOn), not on the form, because form-level errors are not rendered anywhere in the admin and the admin would see only a silent rejection.

DOMESTIC_REVERSE_CHARGE is offered on purpose: the engine never returns it itself, it arises only here.

Parameters
$form : Form

addItemsMultiplier()

The row count limit is read from the raw HTTP data (`onAnchor`, see {@see MultiplierLimits::guard()}), with `$maxCopies` as the second line of defense against materialization.

private addItemsMultiplier(Form $form) : void
Parameters
$form : Form

addLineControls()

private addLineControls(Container $c[, bool $decimalQuantity = false ]) : void
Parameters
$c : Container
$decimalQuantity : bool = false

addNullableSelect()

private addNullableSelect(Form $form, string $name, array<int, NotTranslate> $options, string $promptKey) : SelectBox
Parameters
$form : Form
$name : string
$options : array<int, NotTranslate>
$promptKey : string
Return values
SelectBox

addPickerRefreshButtons()

Hidden `*Refresh` submit buttons: after a pick, `picker.js` clicks them (naja), the server fills the seller/customer snapshot from the picked entity (nested contacts/accounts too) and redraws the block snippet.

private addPickerRefreshButtons(Form $form) : void

validationScope([]) skips validation (a required line does not block it); the createForm gate lets only update/save save, so this does not save. useVat is handled by the client (fieldMap + change, invoice-calc recalculates VAT at once); the server fills only the text fields of the block.

Parameters
$form : Form

addVouchersMultiplier()

Same gate as {@see addItemsMultiplier()}, limit for vouchers.

private addVouchersMultiplier(Form $form) : void
Parameters
$form : Form

applyInvoiceTax()

Applies the invoice VAT from the engine plus the manual override.

private applyInvoiceTax(Invoice $invoice, ArrayHash<string|int, mixed> $values) : bool

Returns false for an incomplete manual override (missing code, rate or reason). Validation catches it earlier (field rules), but an invoice with a half override would pass as "no override" and the admin would never learn it, hence this second, loud gate.

Parameters
$invoice : Invoice
$values : ArrayHash<string|int, mixed>
Return values
bool

applyNewInvoiceDefaults()

New invoice (no template): dates today and today + `duePeriod`, `IN_INVOICE` e-mail defaults.

private applyNewInvoiceDefaults(Form $form) : void
Parameters
$form : Form

buildGifts()

private buildGifts(ArrayHash<string|int, mixed> $values, Invoice $invoice, InvoicePaymentMethod} $originalLines) : ArrayCollection<int, InvoiceGift>
Parameters
$values : ArrayHash<string|int, mixed>
$invoice : Invoice
$originalLines : InvoicePaymentMethod}
Return values
ArrayCollection<int, InvoiceGift>

buildItems()

private buildItems(ArrayHash<string|int, mixed> $values, Invoice $invoice, InvoicePaymentMethod} $originalLines) : ArrayCollection<int, InvoiceItem>
Parameters
$values : ArrayHash<string|int, mixed>
$invoice : Invoice
$originalLines : InvoicePaymentMethod}
Return values
ArrayCollection<int, InvoiceItem>

buildPaymentMethods()

private buildPaymentMethods(ArrayHash<string|int, mixed> $values, Invoice $invoice, InvoicePaymentMethod} $originalLines) : ArrayCollection<int, InvoicePaymentMethod>
Parameters
$values : ArrayHash<string|int, mixed>
$invoice : Invoice
$originalLines : InvoicePaymentMethod}
Return values
ArrayCollection<int, InvoicePaymentMethod>

buildShippers()

private buildShippers(ArrayHash<string|int, mixed> $values, Invoice $invoice, InvoicePaymentMethod} $originalLines) : ArrayCollection<int, InvoiceShipper>
Parameters
$values : ArrayHash<string|int, mixed>
$invoice : Invoice
$originalLines : InvoicePaymentMethod}
Return values
ArrayCollection<int, InvoiceShipper>

buildVouchers()

private buildVouchers(ArrayHash<string|int, mixed> $values, Invoice $invoice, InvoicePaymentMethod} $originalLines) : ArrayCollection<int, InvoiceVoucher>
Parameters
$values : ArrayHash<string|int, mixed>
$invoice : Invoice
$originalLines : InvoicePaymentMethod}
Return values
ArrayCollection<int, InvoiceVoucher>

countryOptions()

Returns ISO country options for the customer select: active countries, CZ and SK first.

private countryOptions() : array<string, string>

A stored (meanwhile deactivated) country stays selectable; otherwise saving would drop it.

Return values
array<string, string>

dateOrNull()

`<input type=date>`: strict parser (see `StrictDateInput`), not `new DateTime()`.

private dateOrNull(mixed $value) : DateTime|null
Parameters
$value : mixed
Return values
DateTime|null

dateOrToday()

Parses a date or falls back to today for a truly empty field.

private dateOrToday(mixed $value) : DateTime

dateRules() (see addInvoiceHeaderFields()) rejects an invalid non-empty value before onSuccess, so only a valid Y-m-d or empty gets here. The fallback is not for an unparsable value (the form already rejected it); creationDate is never empty in the UI because the GET default fills it with today (see applyNewInvoiceDefaults()).

Parameters
$value : mixed
Return values
DateTime

fillMappedFields()

Force-sets form fields from prefill data through a fieldMap (data key to field name; an empty map means the key is the field name, as for the seller). Only existing fields; a checkbox becomes bool.

private fillMappedFields(Form $form, array<string, string> $data, array<string, string> $fieldMap) : void
Parameters
$form : Form
$data : array<string, string>
$fieldMap : array<string, string>

firstItemVat()

Returns the rate of the first line of the invoice, used to prefill the manual override field.

private firstItemVat(Invoice $invoice) : string|null
Parameters
$invoice : Invoice
Return values
string|null

flashInvoiceEmailAfterSave()

Outcome of the e-mail sent by the Api right after the save (`saveInfo.invoiceEmail`); nothing when no e-mail was due (not checked, already sent).

private flashInvoiceEmailAfterSave(mixed $data) : void
Parameters
$data : mixed

giftDefaults()

private giftDefaults(Invoice $invoice) : array<int, array<string, mixed>>
Parameters
$invoice : Invoice
Return values
array<int, array<string, mixed>>

isInvoiceEdit()

Whether a saved invoice (has id) is edited; add and template prefill (transient copy) return false.

private isInvoiceEdit() : bool
Return values
bool

isInvoiceLocked()

Whether the invoice is locked: its own state `lockedDatetime`, set only by the Api (e-mail dispatch, credit note or cancellation of the invoice, explicit lock).

private isInvoiceLocked(Invoice $invoice) : bool

Only UX: saving a locked invoice through the full form is rejected by the Api (InvoiceManager::beforeSave(), ResultCode::INVOICE_LOCKED).

Parameters
$invoice : Invoice
Return values
bool

loadInvoiceSupplyKinds()

Loads the line supply kind options and the default kind (mirror of {@see InvoiceTemplateFormTrait::loadInvoiceTemplateSupplyKinds()}).

private loadInvoiceSupplyKinds() : void

A kind already carried by an invoice line stays offered even if the admin deactivated it meanwhile; otherwise saving would silently drop it.

pickedId()

Reads the picked id safely from the hidden picker field (after submit).

private pickedId(Form $form, string $field) : int
Parameters
$form : Form
$field : string
Return values
int

pickerRefreshButton()

private pickerRefreshButton(Form $form, string $name, callable $apply) : void
Parameters
$form : Form
$name : string
$apply : callable

resolveSupplyKindSellerCountryCode()

Returns the portal seller country (`7/invoice.default.sellerId`) as a fallback for a region without a country; null when the seller is not found.

private resolveSupplyKindSellerCountryCode() : string|null
Return values
string|null

rows()

private rows(mixed $rows) : iterable<string|int, array<string, mixed>|ArrayHash<string|int, mixed>>
Parameters
$rows : mixed
Return values
iterable<string|int, array<string, mixed>|ArrayHash<string|int, mixed>>

saveInvoiceForm()

private saveInvoiceForm(ArrayHash<string|int, mixed> $values) : bool
Parameters
$values : ArrayHash<string|int, mixed>
Return values
bool

saveLockedInvoice()

Saves a locked invoice: only the allowed fields are written by a direct update (no recalculation, no new PDF). Other form values are dropped and the admin gets an explanation.

private saveLockedInvoice(Invoice $invoice, ArrayHash<string|int, mixed> $values) : bool
Parameters
$invoice : Invoice
$values : ArrayHash<string|int, mixed>
Return values
bool

snapshotLineIdentity()

Snapshot of the lines before the full replace, indexed by id, so `build*()` can pull in columns the form does not render (line identity).

private snapshotLineIdentity(Invoice $invoice) : InvoicePaymentMethod}

The form renders one container without id for shipping and payment method, so the first original row is kept; without it the save would delete the row and insert a new one (new id means a lost link to the source invoice).

Parameters
$invoice : Invoice
Return values
InvoicePaymentMethod}

statusOptions()

private statusOptions() : array<string, string>
Return values
array<string, string>

taxTreatmentOptions()

Returns treatment code options for the manual override: translated names from the form domain (`form.fields.taxOverrideCode.codes.<CODE>`), ordered as the enum.

private taxTreatmentOptions() : array<string, NotTranslate>
Return values
array<string, NotTranslate>

trInvoice()

Translates a key in the form domain to a final string (for fields with `setTranslator(null)`).

private trInvoice(string $key) : string
Parameters
$key : string
Return values
string

voucherDefaults()

private voucherDefaults(Invoice $invoice) : array<int, array<string, mixed>>
Parameters
$invoice : Invoice
Return values
array<int, array<string, mixed>>
On this page

Search results