CoolCMS API

ShipperTariffEdit extends BaseComponent
in package
uses AdminComponentFormTrait, FormTrait, FormHelpersTrait, BranchTrait

FinalYes

Add / edit form of a shipper tariff, hosted in the modal of {@see ShipperPresenter} (tab with the tariff list and modal editing; a `Multiplier<ShipperTariffEdit>` keyed by a persistent active key).

The shipper is never a form field: it always comes from the server-side context ($shipperId constructor argument), so a forged shipper id cannot be submitted. The form covers address, branches, texts, price generations (nested Multiplier of generations and bands) and the fixed payment matrix.

Table of Contents

Constants

EDIT_INCLUDES  : array<int, string> = [ 'translations', 'shipper', 'shipperRegion', '...
Canonical includes list, the single source of truth for loading the edited tariff.

Properties

$container  : Container
$routerDispatcher  : RouterDispatcher
$translator  : ITranslator
$appContext  : AppContext
$componentParameters  : array<string, mixed>
$currencyContext  : CurrencyContext|null
$deviceDetector  : DeviceDetector|null
$languageContext  : LanguageContext|null
$localizationContext  : LocalizationContext|null
$settingContext  : SettingContext
$themeContext  : ThemeContext|null
$translatorDomain  : string
$adminLanguageManager  : LanguageManager
$currencyManager  : CurrencyManager
$editedShipperTariff  : ShipperTariff|null
Edited tariff (null when adding); see {@see resolveEditedTariff()}.
$editingBranchId  : int|null
Branch currently edited in the modal (`branchId`); null when adding a new one.
$formActionsFactory  : FormActionsFactory
$formNotices  : array<int, array{type: string, key: string}>
Result-level messages of saving the tariff, rendered directly in the modal content.
$langNavFactory  : LangNavFactory
$paymentMethodManager  : PaymentMethodManager
$shipperId  : int
Edited shipper (server-side context, never taken from the form).
$shipperRegionManager  : ShipperRegionManager
$shipperTariffBandErrors  : array<int, string>
Band validation errors collected while building; `saveShipperTariffForm()` uses them to decide whether saving is allowed at all.
$shipperTariffBandWarnings  : array<int, string>
$shipperTariffCurrencies  : array<int, Currency>
$shipperTariffCurrentAxisDates  : array{prices: ?string, payments: ?string}|null
Date of the currently valid generation of each axis (per-request cache), see {@see getShipperTariffCurrentAxisDates()}.
$shipperTariffGenerationPrefill  : array{bands: list>, payments: array}|null
Prefill for a newly added generation; computed once per request and applied to every empty container that `onCreate` meets.
$shipperTariffManager  : ShipperTariffManager
$shipperTariffPaymentMethods  : array<int, PaymentMethod>
$shipperTariffRegionOptions  : array<int, NotTranslate>
$shipperTariffSupplyKindOptions  : array<int, string>
$shipperTariffVatOptions  : array<int, NotTranslate>
$supplyKindManager  : SupplyKindManager
$tariffId  : int|null
$vatManager  : VatManager

Methods

__call()  : mixed
Dispatches `renderXyz()` to `render('Xyz', ...$args)`.
__construct()  : mixed
canRemoveShipperTariffGeneration()  : bool
Tells whether a generation may be removed: only a new or scheduled one (from today's midnight on). The currently valid one may not, because deleting it would make an older generation take over, a silent return to old prices.
createComponent()  : IComponent|null
getAppContext()  : AppContext
getComponentParameter()  : mixed
Returns a component parameter, or null when it is not set.
getContainer()  : Container
getCurrencyContext()  : CurrencyContext|null
getLanguageContext()  : LanguageContext|null
getLocalizationContext()  : LocalizationContext|null
getRouterDispatcher()  : RouterDispatcher
getSettingContext()  : SettingContext
getSettings()  : array<int, array<string, mixed>>
getShipperTariffBandIssues()  : array<int, array{type: string, from: string, to: string}>
Returns the strips between bands, as `['overlap' => range]` or `['gap' => range]` keyed by the index of the band before which the strip is rendered. It is computed on a sorted copy but returns the index in the submitted order, so the template reorders nothing.
getShipperTariffGenerationBadgeKey()  : string
Returns the badge key of the card header. With migrated data the axes diverge (the price list is valid from another date than the matrix), giving two "current" cards, so the badge says which axis the card holds.
getShipperTariffGenerationState()  : string
Returns `current` (valid now), `scheduled` or `history`, for the badge and card color.
getShipperTariffGenerationSummary()  : array{bands: int, currencies: int, methods: int}
Returns the card header summary: number of bands, number of currencies with at least one price, and number of available payment methods.
getThemeContext()  : ThemeContext|null
handleAddBranch()  : void
handleDeleteBranch()  : void
Deletes a branch over AJAX (no redirect, which would close the open tariff modal).
handleEditBranch()  : void
hasServiceByType()  : bool
Returns whether the container has a service of the given type.
initializeAllContexts()  : void
injectDeviceDetector()  : void
isShipperTariffGenerationDateLocked()  : bool
Tells whether the "valid from" field is locked: for history (fully read-only) and for the currently valid generation, whose values are editable but whose date is a fact; repricing from another date is a new generation. A disabled input is not posted, the build takes the date from `origDateFrom`.
isShipperTariffGenerationOpenByDefault()  : bool
Tells whether the card's `<details>` is open by default: a new generation (empty `origDateFrom`) is open because it is being worked on; all others (valid, scheduled, history) are collapsed.
isShipperTariffGenerationReadOnly()  : bool
Tells whether a generation is read-only. Only superseded history is: a generation before today's midnight that a newer one has replaced on its axis. The currently valid generation (even dated in the past) is editable, and a new one (empty `origDateFrom`) is always editable.
onBranchFormSuccess()  : void
onCreate()  : void
render()  : void
restoreShipperTariffGenerationDisplay()  : void
Restores the display of fields that do not survive a POST followed by an AJAX redraw. The template calls it via `{do}` first in every generation card (before totals are computed).
setAppContext()  : $this
setComponentParameter()  : void
Sets a component parameter.
setCurrencyContext()  : $this
setLanguageContext()  : $this
setLocalizationContext()  : $this
setSettingContext()  : $this
setThemeContext()  : $this
setup()  : void
setupAppContext()  : void
shipperTariffCurrencyHasPrice()  : bool
Tells whether the currency has a price in at least one band of this generation. Used only by {@see getShipperTariffGenerationSummary()} for the currency count; the template lists all currencies for every band regardless.
typedComponent()  : T
Returns a subcomponent with a verified type.
createAdminComponentForm()  : Form
createComponentAdminFormActions()  : FormActions
Creates the save / back buttons of the form.
createComponentAdminLangNav()  : LangNav
Creates the language tabs of the multilingual fields.
createComponentBranchForm()  : Form
createComponentEditForm()  : Form
currentLangId()  : int|null
Returns the current language id (for select captions etc.), null when unknown.
frontPresenter()  : BasePresenter|null
Returns the front presenter the component is attached to (null outside the front end or when detached).
getFormLanguages()  : array<int, Language>
Returns the languages for multilingual form fields (admin `LanguageManager`, constructor-injected).
getTranslator()  : ITranslator
intOrNull()  : int|null
Converts a mixed form value to an int or null (an empty or zero value becomes null).
setTemplateFile()  : void
setTranslator()  : self
str()  : string
Converts a mixed form value to a string; non-scalars become an empty string.
strOrNull()  : string|null
Converts a mixed form value to a string or null (an empty string is stored as null).
addShipperTariffBandControls()  : void
Fields of one weight band inside a generation (currency becomes a table row in the template).
addShipperTariffGenerationMultiplier()  : void
Adds the outer `generations` multiplier, the nested `bands` multiplier and the fixed payment matrix.
addShipperTariffMainFields()  : void
addShipperTariffPaymentMatrixControls()  : void
Adds the payment matrix of a generation: a fixed 2D loop (methods x currencies), not a multiplier, because both axes are code lists that admins do not create in this form.
addShipperTariffTranslationFields()  : void
applyShipperTariffGenerations()  : void
Rebuilds both time series of the tariff from the submitted generations.
buildAfterSave()  : callable
Builds the post-save action (`AdminComponentFormTrait` hook); never redirects the page for AJAX.
buildBranchModalContent()  : string
buildShipperTariffBandDefaultRows()  : array<int, array<string, mixed>>
Builds the band rows of one generation, sorted by `weightFrom` and renumbered (sorting happens on load only).
buildShipperTariffEntity()  : ShipperTariff
buildShipperTariffGenerationDefaultRows()  : array<int, array<string, mixed>>
Maps the existing series to default rows of the outer multiplier.
buildShipperTariffGenerationRowForDate()  : array<string, mixed>
Builds the row of one generation (bands plus payment matrix) for a specific date; shared by {@see buildShipperTariffGenerationDefaultRows()} and {@see duplicateShipperTariffGeneration()}.
buildShipperTariffPaymentMatrixRow()  : array<string, mixed>
Maps the payment matrix cells of one generation to `pmAvailable_<mid>`, `pmPrice_<mid>_<cid>` and `pmFreeFrom_<mid>_<cid>`. Shared by {@see buildShipperTariffGenerationDefaultRows()} (existing generation) and {@see resolveShipperTariffGenerationPrefill()} (prefill of a new one).
canManageBranches()  : bool
Branches can be managed only on an already saved tariff (the foreign key target must exist).
collectShipperTariffBands()  : array<int, array{weightFrom: string, weightTo: ?string, row: array}>
Extracts and validates the bands of one generation. The returned order is the submitted order, not the sorted one: validation runs on a sorted copy so that error messages can name the exact range while the user's rows are not rearranged.
createMultiplierComponent()  : Multiplier<string|int, IComponent>
Creates a multiplier for the component.
createNewComponentInstance()  : IComponent|null
Creates a new component instance without adding it to the presenter; used by the multiplier.
createStandardComponent()  : IComponent|null
Creates a standard component (not a multiplier).
decimalOrNull()  : string|null
NOTE: this is not a duplicate of {@see \App\Core\Utils\Helpers\Decimal::fromInput()}. It quantizes to a given number of decimal places (`$scale`) instead of normalizing input. The scale differs per column (weight vs. shipping price), so it does not belong in the shared helper, which deliberately knows no scale so it cannot be unified across modules by accident.
duplicateShipperTariffGeneration()  : Container|null
Handles the "duplicate" action in the card header: copies the whole stored generation (bands and matrix) into a new generation so the admin only adjusts the date and prices.
entityReference()  : Shipper
Builds a thin shipper reference (the hydrator assembles the entity from the id).
fillShipperTariffGenerationContainer()  : void
Fills a generation container with row data (format of {@see buildShipperTariffGenerationRowForDate()}) so that it also works on a submitted form; the only reliable path for the AJAX actions "add generation" (prefill) and "duplicate".
findBranch()  : ShipperTariffBranch|null
flashBranchGuardError()  : void
formatBranchAddress()  : string
getShipperTariffCurrentAxisDates()  : array{prices: ?string, payments: ?string}
Returns the date of the currently valid generation of each axis separately (the greatest `date_from <= now`) in the `Y-m-d H:i:s` format that the hidden `origDateFrom` carries.
httpRequest()  : IRequest
Returns the HTTP request through the presenter; a control, unlike a presenter, has no `getHttpRequest()`.
isOwnEditFormSubmit()  : bool
Tells whether this request is a POST submit of this very `editForm`. The `_do` value carries the full component path, so a component created after the key switch has a different path and safely gets defaults.
isShipperTariffGenerationEmpty()  : bool
Detects a new empty container by the absence of data, not by index: `onCreate` runs for all generation containers on every request, including history and half-edited generations after a redraw. Empty `origDateFrom` alone is not enough (a new generation has it empty even after the admin typed something), so all fields the prefill would overwrite are checked too.
isShipperTariffHistoric()  : bool
loadBranchRows()  : array<int, array{id: int, name: string, address: string, active: bool}>
Builds the branch cards, always filtered by `isManaged`: `false` is reserved for a future bulk sync of an external shipper network and is never listed in the admin.
loadShipperTariffSelectData()  : void
Loads the form axes (regions, VAT rates, currencies, payment methods) once per request; called from `createComponentEditForm()` before the fields and defaults are built.
prefillNewShipperTariffGeneration()  : void
Prefills a newly added generation with the data of the currently valid one. Leaves `validFrom` and `origDateFrom` alone: empty `origDateFrom` marks a new generation for rendering and the immutability guard, and `validFrom` must be chosen deliberately by the admin.
redrawBranchesAfterMutation()  : void
redrawTariffModal()  : void
Redraws the host snippet `tariffEditHost` after an AJAX validation or save failure. The component renders again with the same submitted form (inline field errors) plus flash, and the modal stays open.
resolveEditedTariff()  : ShipperTariff|null
Loads the edited tariff, or returns null when adding.
resolveShipperTariffGenerationDate()  : DateTime|null
Converts `validFrom` to a `DateTime`. Empty means effective immediately (now). A past date would write into history, so it is an error, not a silent shift to today.
resolveShipperTariffGenerationPrefill()  : array{bands: list>, payments: array}
Resolves the prefill data of a new generation. Bands and the payment matrix are looked up separately because they are two independent time series ({@see ShipperTariff}).
resolveShipperTariffPrefillDate()  : DateTime|null
Resolves the generation date for the prefill: the greatest `date_from <= now` (the currently valid generation), or the nearest future one when the whole price list is only scheduled.
saveShipperTariffForm()  : bool
setBranchFormDefaults()  : void
setShipperTariffFormDefaults()  : void
shipperPresenter()  : ShipperPresenter|null
Returns the host presenter (the only consumer of this component), or null for any other.
shipperTariffBandContainers()  : array<int, Container>
shipperTariffControlValue()  : mixed
Returns a container control's value without type juggling over `IComponent` (which has no `getValue()`).
shipperTariffPaymentMethodRef()  : PaymentMethod
Builds a thin payment method reference (the hydrator assembles the entity from the id).
shipperTariffValuesToArray()  : array<string, mixed>
Converts a form container (`ArrayHash` from the multiplier) to a plain array so that typed offsets can be used. Anything other than an array or `ArrayHash` is an empty row.
showBranchModal()  : void
translateShipperTariff()  : string
validateShipperTariffBandCoverage()  : void
Validates band invariants within one generation: at most one open band, the open band must be the topmost, no overlaps (error) and ideally no gaps (warning only, a carrier may legitimately not ship every weight).

Constants

EDIT_INCLUDES

Canonical includes list, the single source of truth for loading the edited tariff.

public array<int, string> EDIT_INCLUDES = [ 'translations', 'shipper', 'shipperRegion', 'prices', 'prices.vat', 'paymentPrices', 'paymentPrices.paymentMethod', // IMPORTANT: branches must be included even though they are saved by their own path // (`BranchTrait::handleAddBranch()` etc., outside the main form submit); an owned // collection with orphan removal would otherwise be deleted on every save of the // main form. 'branches', ]

Properties

$componentParameters

protected array<string, mixed> $componentParameters = []

component parameters set by setComponentParameter()

$translatorDomain

protected string $translatorDomain = 'admin.eshop.presenters.shipperTariff'

$editingBranchId

Branch currently edited in the modal (`branchId`); null when adding a new one.

private int|null $editingBranchId = null

$formNotices

Result-level messages of saving the tariff, rendered directly in the modal content.

private array<int, array{type: string, key: string}> $formNotices = []

Per-request buffer.

IMPORTANT: presenter flashMessage() is not visible in a component template and the page has no admin flashMessagesSnippet, so flashed messages would be lost.

$shipperId read-only

Edited shipper (server-side context, never taken from the form).

private int $shipperId

$shipperTariffBandErrors

Band validation errors collected while building; `saveShipperTariffForm()` uses them to decide whether saving is allowed at all.

private array<int, string> $shipperTariffBandErrors = []

$shipperTariffBandWarnings

private array<int, string> $shipperTariffBandWarnings = []

warnings (gaps between bands); flashed, do not block saving

$shipperTariffCurrentAxisDates

Date of the currently valid generation of each axis (per-request cache), see {@see getShipperTariffCurrentAxisDates()}.

private array{prices: ?string, payments: ?string}|null $shipperTariffCurrentAxisDates = null

$shipperTariffGenerationPrefill

Prefill for a newly added generation; computed once per request and applied to every empty container that `onCreate` meets.

private array{bands: list>, payments: array}|null $shipperTariffGenerationPrefill = null

$shipperTariffPaymentMethods

private array<int, PaymentMethod> $shipperTariffPaymentMethods = []

payment matrix axis, including unavailable methods (they can be enabled again)

$shipperTariffRegionOptions

private array<int, NotTranslate> $shipperTariffRegionOptions = []

$shipperTariffSupplyKindOptions

private array<int, string> $shipperTariffSupplyKindOptions = []

supply kinds (id => name from the DB; the select has the translator turned off)

$shipperTariffVatOptions

private array<int, NotTranslate> $shipperTariffVatOptions = []

Methods

__call()

Dispatches `renderXyz()` to `render('Xyz', ...$args)`.

public __call(string $name, array<string|int, mixed> $args) : mixed

Called only when the component has no renderXyz() method of its own.

Parameters
$name : string
$args : array<string|int, mixed>

__construct()

public __construct(ShipperTariffManager $shipperTariffManager, ShipperRegionManager $shipperRegionManager, CurrencyManager $currencyManager, VatManager $vatManager, PaymentMethodManager $paymentMethodManager, LanguageManager $adminLanguageManager, LangNavFactory $langNavFactory, FormActionsFactory $formActionsFactory, SupplyKindManager $supplyKindManager, int|null $tariffId, int $shipperId) : mixed
Parameters
$shipperTariffManager : ShipperTariffManager
$shipperRegionManager : ShipperRegionManager
$currencyManager : CurrencyManager
$vatManager : VatManager
$paymentMethodManager : PaymentMethodManager
$adminLanguageManager : LanguageManager
$langNavFactory : LangNavFactory
$formActionsFactory : FormActionsFactory
$supplyKindManager : SupplyKindManager
$tariffId : int|null
$shipperId : int

canRemoveShipperTariffGeneration()

Tells whether a generation may be removed: only a new or scheduled one (from today's midnight on). The currently valid one may not, because deleting it would make an older generation take over, a silent return to old prices.

public canRemoveShipperTariffGeneration(Container $generation) : bool
Parameters
$generation : Container
Return values
bool

createComponent()

public createComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

getComponentParameter()

Returns a component parameter, or null when it is not set.

public getComponentParameter(string $name) : mixed
Parameters
$name : string

getContainer()

public getContainer() : Container
Return values
Container

getSettings()

public getSettings() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>

getShipperTariffBandIssues()

Returns the strips between bands, as `['overlap' => range]` or `['gap' => range]` keyed by the index of the band before which the strip is rendered. It is computed on a sorted copy but returns the index in the submitted order, so the template reorders nothing.

public getShipperTariffBandIssues(Container $generation) : array<int, array{type: string, from: string, to: string}>
Parameters
$generation : Container
Return values
array<int, array{type: string, from: string, to: string}>

getShipperTariffGenerationBadgeKey()

Returns the badge key of the card header. With migrated data the axes diverge (the price list is valid from another date than the matrix), giving two "current" cards, so the badge says which axis the card holds.

public getShipperTariffGenerationBadgeKey(Container $generation) : string
Parameters
$generation : Container
Return values
string

getShipperTariffGenerationState()

Returns `current` (valid now), `scheduled` or `history`, for the badge and card color.

public getShipperTariffGenerationState(Container $generation) : string
Parameters
$generation : Container
Return values
string

getShipperTariffGenerationSummary()

Returns the card header summary: number of bands, number of currencies with at least one price, and number of available payment methods.

public getShipperTariffGenerationSummary(Container $generation) : array{bands: int, currencies: int, methods: int}
Parameters
$generation : Container
Return values
array{bands: int, currencies: int, methods: int}

handleDeleteBranch()

Deletes a branch over AJAX (no redirect, which would close the open tariff modal).

public handleDeleteBranch(int $branchId) : void

The confirmation dialog comes from the template (data-grid-confirm); its JS handler is delegated on document, so it works on any link.

Parameters
$branchId : int
Attributes
#[Requires]
$methods: 'POST'

handleEditBranch()

public handleEditBranch(int $branchId) : void
Parameters
$branchId : int

hasServiceByType()

Returns whether the container has a service of the given type.

public hasServiceByType(string $className) : bool

The name is composed dynamically (module plus component), so class_exists() runs first, and a true result guarantees a valid type.

Parameters
$className : string
Tags
phpstan-assert-if-true

class-string $className

Return values
bool

isShipperTariffGenerationDateLocked()

Tells whether the "valid from" field is locked: for history (fully read-only) and for the currently valid generation, whose values are editable but whose date is a fact; repricing from another date is a new generation. A disabled input is not posted, the build takes the date from `origDateFrom`.

public isShipperTariffGenerationDateLocked(Container $generation) : bool
Parameters
$generation : Container
Return values
bool

isShipperTariffGenerationOpenByDefault()

Tells whether the card's `<details>` is open by default: a new generation (empty `origDateFrom`) is open because it is being worked on; all others (valid, scheduled, history) are collapsed.

public isShipperTariffGenerationOpenByDefault(Container $generation) : bool
Parameters
$generation : Container
Return values
bool

isShipperTariffGenerationReadOnly()

Tells whether a generation is read-only. Only superseded history is: a generation before today's midnight that a newer one has replaced on its axis. The currently valid generation (even dated in the past) is editable, and a new one (empty `origDateFrom`) is always editable.

public isShipperTariffGenerationReadOnly(Container $generation) : bool

The Api immutability guard enforces the same rule.

Parameters
$generation : Container
Return values
bool

onBranchFormSuccess()

public onBranchFormSuccess(Form $form, ArrayHash<string|int, mixed> $values) : void
Parameters
$form : Form
$values : ArrayHash<string|int, mixed>

restoreShipperTariffGenerationDisplay()

Restores the display of fields that do not survive a POST followed by an AJAX redraw. The template calls it via `{do}` first in every generation card (before totals are computed).

public restoreShipperTariffGenerationDisplay(Container $generation) : void

Disabled fields are not submitted and defaults are applied on GET only, so after any AJAX action (duplicate, add or remove band) they would render empty:

  • the locked date of the valid generation is restored from the hidden origDateFrom;
  • read-only history has disabled selects (VAT) and checkboxes, so the whole row is restored from stored data (its POST is ignored on build anyway).

Posted values of editable fields are never overwritten.

Parameters
$generation : Container

setComponentParameter()

Sets a component parameter.

public setComponentParameter(string $name, mixed $value) : void
Parameters
$name : string
$value : mixed

shipperTariffCurrencyHasPrice()

Tells whether the currency has a price in at least one band of this generation. Used only by {@see getShipperTariffGenerationSummary()} for the currency count; the template lists all currencies for every band regardless.

public shipperTariffCurrencyHasPrice(Container $generation, int $currencyId) : bool
Parameters
$generation : Container
$currencyId : int
Return values
bool

typedComponent()

Returns a subcomponent with a verified type.

public typedComponent(string $name, T> $type) : T

Use it instead of an inline @var above $this['x'] (which does not narrow the type) and instead of the generic IComponent that $this['x'] returns. See Form::typedComponent().

Parameters
$name : string
$type : T>
Tags
template
Return values
T

createAdminComponentForm()

protected createAdminComponentForm(callable(ArrayHash<string|int, mixed>, Form=): bool $processData[, callable(string): void|null $afterSave = null ]) : Form
Parameters
$processData : callable(ArrayHash<string|int, mixed>, Form=): bool

Saves the data; returns success (handles its own flash).

$afterSave : callable(string): void|null = null

When set, replaces the default backKey/redirect logic after a successful $processData; the parameter is the clicked submit name (update or save). The hidden backKey stays in the form but is unused. For a component hosted in a modal shell, where a page redirect makes no sense (the modal is an AJAX shell over the presenter page).

Return values
Form

createComponentAdminLangNav()

Creates the language tabs of the multilingual fields.

protected createComponentAdminLangNav() : LangNav
Return values
LangNav

createComponentBranchForm()

protected createComponentBranchForm() : Form
Return values
Form

createComponentEditForm()

protected createComponentEditForm() : Form
Return values
Form

currentLangId()

Returns the current language id (for select captions etc.), null when unknown.

protected currentLangId() : int|null
Return values
int|null

frontPresenter()

Returns the front presenter the component is attached to (null outside the front end or when detached).

protected frontPresenter() : BasePresenter|null
Return values
BasePresenter|null

getFormLanguages()

Returns the languages for multilingual form fields (admin `LanguageManager`, constructor-injected).

protected getFormLanguages() : array<int, Language>
Return values
array<int, Language>

getTranslator()

protected getTranslator() : ITranslator
Return values
ITranslator

intOrNull()

Converts a mixed form value to an int or null (an empty or zero value becomes null).

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

setTemplateFile()

protected setTemplateFile(string $dir, string $filename) : void
Parameters
$dir : string
$filename : string

setTranslator()

protected setTranslator(ITranslator $translator) : self
Parameters
$translator : ITranslator
Return values
self

str()

Converts a mixed form value to a string; non-scalars become an empty string.

protected str(mixed $value) : string
Parameters
$value : mixed
Return values
string

strOrNull()

Converts a mixed form value to a string or null (an empty string is stored as null).

protected strOrNull(mixed $value) : string|null
Parameters
$value : mixed
Return values
string|null

addShipperTariffBandControls()

Fields of one weight band inside a generation (currency becomes a table row in the template).

private addShipperTariffBandControls(Container $container) : void
Parameters
$container : Container

addShipperTariffGenerationMultiplier()

Adds the outer `generations` multiplier, the nested `bands` multiplier and the fixed payment matrix.

private addShipperTariffGenerationMultiplier(Form $form) : void

IMPORTANT: the redraw closure is shared and always redraws the outer generations snippet, also for the nested band buttons. A nested multiplier cannot have its own snippet (it would sit inside the outer one and its name could not be a literal per index).

Parameters
$form : Form

addShipperTariffMainFields()

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

addShipperTariffPaymentMatrixControls()

Adds the payment matrix of a generation: a fixed 2D loop (methods x currencies), not a multiplier, because both axes are code lists that admins do not create in this form.

private addShipperTariffPaymentMatrixControls(Container $generation) : void

pmAvailable_<mid> gates the whole method row across currencies; payment method availability is a tariff property, not a currency one.

Parameters
$generation : Container

addShipperTariffTranslationFields()

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

applyShipperTariffGenerations()

Rebuilds both time series of the tariff from the submitted generations.

private applyShipperTariffGenerations(ArrayHash<string|int, mixed> $values, ShipperTariff $tariff) : void

Superseded history (older than the currently valid generation of its axis) is carried over as the original entities; it must be returned to the collection, otherwise orphanRemoval deletes it. Editable generations are rebuilt from scratch as a snapshot: every band x currency with a price and every method x currency combination (even unavailable ones) is written.

IMPORTANT: the currently valid generation is editable; only history already replaced by a newer generation is locked. The date of the valid generation cannot be changed (the input is disabled, the build takes it from origDateFrom); repricing from a date is a new generation.

Parameters
$values : ArrayHash<string|int, mixed>
$tariff : ShipperTariff

buildAfterSave()

Builds the post-save action (`AdminComponentFormTrait` hook); never redirects the page for AJAX.

private buildAfterSave() : callable
  • save (AJAX): closes the modal.
  • update (AJAX): keeps the modal open and redraws the component (fresh $editedShipperTariff, see saveShipperTariffForm()); always also redraws the tariff list and flash.
  • non-AJAX fallback: full redirect to the tariffs tab.
Return values
callable

buildBranchModalContent()

private buildBranchModalContent() : string
Return values
string

buildShipperTariffBandDefaultRows()

Builds the band rows of one generation, sorted by `weightFrom` and renumbered (sorting happens on load only).

private buildShipperTariffBandDefaultRows(array<int, ShipperTariffPrice> $prices) : array<int, array<string, mixed>>
Parameters
$prices : array<int, ShipperTariffPrice>
Return values
array<int, array<string, mixed>>

buildShipperTariffEntity()

private buildShipperTariffEntity(ArrayHash<string|int, mixed> $values) : ShipperTariff
Parameters
$values : ArrayHash<string|int, mixed>
Tags
phpstan-impure

Fills $shipperTariffBandErrors and $shipperTariffBandWarnings.

Return values
ShipperTariff

buildShipperTariffGenerationDefaultRows()

Maps the existing series to default rows of the outer multiplier.

private buildShipperTariffGenerationDefaultRows(ShipperTariff $tariff) : array<int, array<string, mixed>>

Rows are grouped by date_from (both series separately but into the same generation, as they share the date; a generation with only a payment matrix and no bands must still be shown). Generations are ordered date_from descending (newest first) and future (scheduled) ones are shown too. Each row is built by buildShipperTariffGenerationRowForDate(), shared with duplicateShipperTariffGeneration().

Parameters
$tariff : ShipperTariff
Return values
array<int, array<string, mixed>>

buildShipperTariffGenerationRowForDate()

Builds the row of one generation (bands plus payment matrix) for a specific date; shared by {@see buildShipperTariffGenerationDefaultRows()} and {@see duplicateShipperTariffGeneration()}.

private buildShipperTariffGenerationRowForDate(ShipperTariff $tariff, string $dateKey, array<int, ShipperTariffPrice> $prices, array<int, PaymentMethodPrice> $payments) : array<string, mixed>
Parameters
$tariff : ShipperTariff
$dateKey : string
$prices : array<int, ShipperTariffPrice>
$payments : array<int, PaymentMethodPrice>
Return values
array<string, mixed>

buildShipperTariffPaymentMatrixRow()

Maps the payment matrix cells of one generation to `pmAvailable_<mid>`, `pmPrice_<mid>_<cid>` and `pmFreeFrom_<mid>_<cid>`. Shared by {@see buildShipperTariffGenerationDefaultRows()} (existing generation) and {@see resolveShipperTariffGenerationPrefill()} (prefill of a new one).

private buildShipperTariffPaymentMatrixRow(array<int, PaymentMethodPrice> $paymentPrices) : array<string, mixed>
Parameters
$paymentPrices : array<int, PaymentMethodPrice>
Return values
array<string, mixed>

canManageBranches()

Branches can be managed only on an already saved tariff (the foreign key target must exist).

private canManageBranches() : bool
Return values
bool

collectShipperTariffBands()

Extracts and validates the bands of one generation. The returned order is the submitted order, not the sorted one: validation runs on a sorted copy so that error messages can name the exact range while the user's rows are not rearranged.

private collectShipperTariffBands(array<string, mixed> $generation, string $dateKey) : array<int, array{weightFrom: string, weightTo: ?string, row: array}>
Parameters
$generation : array<string, mixed>
$dateKey : string
Tags
phpstan-impure

Fills $shipperTariffBandErrors and $shipperTariffBandWarnings.

Return values
array<int, array{weightFrom: string, weightTo: ?string, row: array}>

createMultiplierComponent()

Creates a multiplier for the component.

private createMultiplierComponent(string $name) : Multiplier<string|int, IComponent>
Parameters
$name : string
Return values
Multiplier<string|int, IComponent>

createNewComponentInstance()

Creates a new component instance without adding it to the presenter; used by the multiplier.

private createNewComponentInstance(string $baseName[, int|string|null $uniqueKey = null ]) : IComponent|null
Parameters
$baseName : string
$uniqueKey : int|string|null = null
Return values
IComponent|null

createStandardComponent()

Creates a standard component (not a multiplier).

private createStandardComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

decimalOrNull()

NOTE: this is not a duplicate of {@see \App\Core\Utils\Helpers\Decimal::fromInput()}. It quantizes to a given number of decimal places (`$scale`) instead of normalizing input. The scale differs per column (weight vs. shipping price), so it does not belong in the shared helper, which deliberately knows no scale so it cannot be unified across modules by accident.

private decimalOrNull(mixed $value, int $scale) : string|null
Parameters
$value : mixed
$scale : int
Return values
string|null

duplicateShipperTariffGeneration()

Handles the "duplicate" action in the card header: copies the whole stored generation (bands and matrix) into a new generation so the admin only adjusts the date and prices.

private duplicateShipperTariffGeneration(Container $generation) : Container|null

Defaults are built from the stored $this->editedShipperTariff filtered by this card's origDateFrom, not from the POST: readonly/disabled history fields are not fully posted.

The redraw is not part of this method; the onClick handler in addShipperTariffGenerationMultiplier() calls it separately, so the method can run outside an attached presenter, where redrawControl() has nothing to redraw.

Parameters
$generation : Container
Return values
Container|null

entityReference()

Builds a thin shipper reference (the hydrator assembles the entity from the id).

private entityReference(int $shipperId) : Shipper
Parameters
$shipperId : int
Return values
Shipper

fillShipperTariffGenerationContainer()

Fills a generation container with row data (format of {@see buildShipperTariffGenerationRowForDate()}) so that it also works on a submitted form; the only reliable path for the AJAX actions "add generation" (prefill) and "duplicate".

private fillShipperTariffGenerationContainer(Container $generation, array<string, mixed> $row) : void

A plain setValues($row) is not enough: Multiplier::setValues() only stores the bands values and builds copies later in createCopies(), which on a submitted form builds them from the HTTP data; a new container has none, so no bands appear. Bands are therefore created explicitly with addCopy($i) plus setValues($bandRow) on every copy.

Parameters
$generation : Container
$row : array<string, mixed>

flashBranchGuardError()

private flashBranchGuardError(string $messageKey) : void
Parameters
$messageKey : string

getShipperTariffCurrentAxisDates()

Returns the date of the currently valid generation of each axis separately (the greatest `date_from <= now`) in the `Y-m-d H:i:s` format that the hidden `origDateFrom` carries.

private getShipperTariffCurrentAxisDates() : array{prices: ?string, payments: ?string}

null means the axis has no valid generation.

Return values
array{prices: ?string, payments: ?string}

httpRequest()

Returns the HTTP request through the presenter; a control, unlike a presenter, has no `getHttpRequest()`.

private httpRequest() : IRequest
Return values
IRequest

isOwnEditFormSubmit()

Tells whether this request is a POST submit of this very `editForm`. The `_do` value carries the full component path, so a component created after the key switch has a different path and safely gets defaults.

private isOwnEditFormSubmit() : bool
Return values
bool

isShipperTariffGenerationEmpty()

Detects a new empty container by the absence of data, not by index: `onCreate` runs for all generation containers on every request, including history and half-edited generations after a redraw. Empty `origDateFrom` alone is not enough (a new generation has it empty even after the admin typed something), so all fields the prefill would overwrite are checked too.

private isShipperTariffGenerationEmpty(Container $generation) : bool
Parameters
$generation : Container
Return values
bool

isShipperTariffHistoric()

private isShipperTariffHistoric(DateTime|null $dateFrom, DateTime $todayMidnight) : bool
Parameters
$dateFrom : DateTime|null
$todayMidnight : DateTime
Return values
bool

loadBranchRows()

Builds the branch cards, always filtered by `isManaged`: `false` is reserved for a future bulk sync of an external shipper network and is never listed in the admin.

private loadBranchRows() : array<int, array{id: int, name: string, address: string, active: bool}>
Return values
array<int, array{id: int, name: string, address: string, active: bool}>

loadShipperTariffSelectData()

Loads the form axes (regions, VAT rates, currencies, payment methods) once per request; called from `createComponentEditForm()` before the fields and defaults are built.

private loadShipperTariffSelectData() : void

The shipper options are not loaded because the shipper is server-side context.

prefillNewShipperTariffGeneration()

Prefills a newly added generation with the data of the currently valid one. Leaves `validFrom` and `origDateFrom` alone: empty `origDateFrom` marks a new generation for rendering and the immutability guard, and `validFrom` must be chosen deliberately by the admin.

private prefillNewShipperTariffGeneration(Container $generation) : void
Parameters
$generation : Container

redrawBranchesAfterMutation()

private redrawBranchesAfterMutation() : void

redrawTariffModal()

Redraws the host snippet `tariffEditHost` after an AJAX validation or save failure. The component renders again with the same submitted form (inline field errors) plus flash, and the modal stays open.

private redrawTariffModal() : void

resolveEditedTariff()

Loads the edited tariff, or returns null when adding.

private resolveEditedTariff() : ShipperTariff|null

The shipper.id filter is the second layer of the ownership guard (the first is ShipperPresenter::ownsTariff() before the modal opens): a forged foreign id is simply not found.

Return values
ShipperTariff|null

resolveShipperTariffGenerationDate()

Converts `validFrom` to a `DateTime`. Empty means effective immediately (now). A past date would write into history, so it is an error, not a silent shift to today.

private resolveShipperTariffGenerationDate(array<string, mixed> $generation, DateTime $todayMidnight) : DateTime|null
Parameters
$generation : array<string, mixed>
$todayMidnight : DateTime
Tags
phpstan-impure

Fills $shipperTariffBandErrors.

Return values
DateTime|null

resolveShipperTariffGenerationPrefill()

Resolves the prefill data of a new generation. Bands and the payment matrix are looked up separately because they are two independent time series ({@see ShipperTariff}).

private resolveShipperTariffGenerationPrefill(ShipperTariff $tariff) : array{bands: list>, payments: array}
Parameters
$tariff : ShipperTariff
Return values
array{bands: list>, payments: array}

resolveShipperTariffPrefillDate()

Resolves the generation date for the prefill: the greatest `date_from <= now` (the currently valid generation), or the nearest future one when the whole price list is only scheduled.

private resolveShipperTariffPrefillDate(array<int, DateTime> $dates, DateTime $now) : DateTime|null

Empty input (a series without any price) returns null and the caller skips the prefill for that axis.

Parameters
$dates : array<int, DateTime>
$now : DateTime
Return values
DateTime|null

saveShipperTariffForm()

private saveShipperTariffForm(ArrayHash<string|int, mixed> $values, Form|null $form) : bool
Parameters
$values : ArrayHash<string|int, mixed>
$form : Form|null
Return values
bool

setBranchFormDefaults()

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

setShipperTariffFormDefaults()

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

shipperTariffBandContainers()

private shipperTariffBandContainers(Container $generation) : array<int, Container>
Parameters
$generation : Container
Return values
array<int, Container>

shipperTariffControlValue()

Returns a container control's value without type juggling over `IComponent` (which has no `getValue()`).

private shipperTariffControlValue(Container $container, string $name) : mixed
Parameters
$container : Container
$name : string

shipperTariffPaymentMethodRef()

Builds a thin payment method reference (the hydrator assembles the entity from the id).

private shipperTariffPaymentMethodRef(int $methodId) : PaymentMethod
Parameters
$methodId : int
Return values
PaymentMethod

shipperTariffValuesToArray()

Converts a form container (`ArrayHash` from the multiplier) to a plain array so that typed offsets can be used. Anything other than an array or `ArrayHash` is an empty row.

private shipperTariffValuesToArray(mixed $values) : array<string, mixed>
Parameters
$values : mixed
Return values
array<string, mixed>

translateShipperTariff()

private translateShipperTariff(string $key[, array<string, string> $params = [] ]) : string
Parameters
$key : string
$params : array<string, string> = []
Return values
string

validateShipperTariffBandCoverage()

Validates band invariants within one generation: at most one open band, the open band must be the topmost, no overlaps (error) and ideally no gaps (warning only, a carrier may legitimately not ship every weight).

private validateShipperTariffBandCoverage(array<int, array{weightFrom: string, weightTo: ?string, row: array}> $bands, string $dateKey) : void
Parameters
$bands : array<int, array{weightFrom: string, weightTo: ?string, row: array}>
$dateKey : string
Tags
phpstan-impure

Fills $shipperTariffBandErrors and $shipperTariffBandWarnings.

On this page

Search results