CoolCMS API

StockReader
in package

FinalYes

Read model for stock availability and delivery estimates. Pure reads, no locks.

The batch API runs a constant number of SQL queries regardless of the number of items (aggregation over levels, warehouses, items, suppliers and vacations), so an e-shop can query a whole category listing cheaply.

Delivery-days algorithm (deterministic; the vacation is opt-out per warehouse):

  • D_shift starts at today; while it falls within an active vacation it moves to the vacation end_date + 1 (re-scanning handles consecutive windows).
  • For each active warehouse with available > 0: D_w is D_shift when the warehouse has vacation_affected, otherwise today; ship_w = D_w + delivery_days.
  • With available > 0, shipping is min(ship_w). Otherwise, if the item has a supplier with delivery_days, shipping is D_shift + supplier.delivery_days. Otherwise deliveryDays is null ("on request").
  • deliveryDays is shipping minus today, in days.

A vacation shifts the shipping day, not the transit: a window starting after the shipping day never changes the estimate, per warehouse. labelWarehouseId is the first warehouse by ascending order with available > 0; it labels the stock source, not the speed.

IMPORTANT: the min() runs over the resulting shipping dates, not over delivery_days. With a mix of vacation_affected warehouses the faster warehouse can be the slower one on paper (see Warehouse::$vacationAffected).

Table of Contents

Constants

DECIMAL_SCALE  : int = 3
ITEMS_TABLE  : string = 'cms_mod_store_stock_items'
LEVELS_TABLE  : string = 'cms_mod_store_stock_levels'
SUPPLIERS_TABLE  : string = 'cms_mod_store_suppliers'
VACATIONS_TABLE  : string = 'cms_mod_store_vacations'
WAREHOUSES_TABLE  : string = 'cms_mod_store_warehouses'

Properties

$em  : EntityManagerInterface
$vacationWindowsMemo  : array<int, DateTimeImmutable}>|null
Request-scoped memo of active vacation windows. It does not depend on item ids or on time ("today" is computed in PHP). `VacationManager` invalidates it through {@see self::resetMemo()} after every write. Stock state (levels, reservations) is never memoized here, because it changes within one request.

Methods

__construct()  : mixed
findItemByCode()  : StockItem|null
findItemByExternalRef()  : StockItem|null
getAvailability()  : ItemAvailability|null
getAvailabilityBatch()  : array<int, ItemAvailability>
getAvailabilityByExternal()  : array<int, ItemAvailability>
getBatches()  : array<int, StockBatch>
Live batch layers of a card (optionally one warehouse), ordered FEFO with FIFO fallback (expiration ascending with null last, then received, then id ascending).
getVacationNotice()  : array<int, DateTimeImmutable}>
Returns active and upcoming vacations with an indicative delivery date after each ends.
resetMemo()  : void
Drops the memoized vacation windows (called by `VacationManager` after a write or delete).
buildAvailability()  : ItemAvailability
A deactivated stock item is withdrawn from sale: the physical totals and the warehouse rows stay as they are, but nothing is available, there is no delivery estimate and no label warehouse.
chainPastWindows()  : DateTimeImmutable
Moves `$day` past every window it falls into, re-scanning so that consecutive vacations chain (a shifted day may fall into the next window).
daysBetween()  : int
decimal()  : numeric-string
loadActiveVacationWindows()  : array<int, DateTimeImmutable}>
Returns active vacation windows (one query). Shared by the delivery algorithm and by the banner helper {@see self::getVacationNotice()}, so there is a single definition of "active vacation" and the two cannot drift apart.
resolveDelivery()  : array{0: ?int, 1: ?int}
Resolves delivery days and the label warehouse. The vacation shift is computed per warehouse (`vacation_affected`) before `min()`, because a warehouse without the checkbox ships during the vacation too. The minimum is therefore taken over the resulting shipping dates, not over `delivery_days` (a warehouse with a longer lead time but no vacation can deliver sooner than a fast warehouse waiting for the vacation to end).
resolveShippingDay()  : DateTimeImmutable
Shift today past any active vacation window(s), including consecutive ones.
resolveVacationAffectedDeliveryDays()  : int|null
Returns `min(delivery_days)` over active warehouses affected by the vacation (`vacation_affected = 1`). Null means no such warehouse exists (all have the checkbox off or are inactive), so the banner shows no date because no warehouse waits for the vacation to end.
toDay()  : DateTimeImmutable|null
toInt()  : int

Constants

ITEMS_TABLE

private string ITEMS_TABLE = 'cms_mod_store_stock_items'

LEVELS_TABLE

private string LEVELS_TABLE = 'cms_mod_store_stock_levels'

SUPPLIERS_TABLE

private string SUPPLIERS_TABLE = 'cms_mod_store_suppliers'

VACATIONS_TABLE

private string VACATIONS_TABLE = 'cms_mod_store_vacations'

WAREHOUSES_TABLE

private string WAREHOUSES_TABLE = 'cms_mod_store_warehouses'

Properties

$vacationWindowsMemo

Request-scoped memo of active vacation windows. It does not depend on item ids or on time ("today" is computed in PHP). `VacationManager` invalidates it through {@see self::resetMemo()} after every write. Stock state (levels, reservations) is never memoized here, because it changes within one request.

private array<int, DateTimeImmutable}>|null $vacationWindowsMemo = null

Methods

__construct()

public __construct(EntityManagerInterface $em) : mixed
Parameters
$em : EntityManagerInterface

findItemByExternalRef()

public findItemByExternalRef(int $externalModule, int $externalId) : StockItem|null
Parameters
$externalModule : int
$externalId : int
Return values
StockItem|null

getAvailabilityBatch()

public getAvailabilityBatch(array<string|int, int> $stockItemIds) : array<int, ItemAvailability>
Parameters
$stockItemIds : array<string|int, int>
Return values
array<int, ItemAvailability> —

keyed by stockItemId (only existing items)

getAvailabilityByExternal()

public getAvailabilityByExternal(int $externalModule, array<string|int, int> $externalIds) : array<int, ItemAvailability>
Parameters
$externalModule : int
$externalIds : array<string|int, int>
Return values
array<int, ItemAvailability> —

keyed by externalId (only mapped items). On multiple cards mapped to the same externalId, the lowest stock_item_id wins + a warning log.

getBatches()

Live batch layers of a card (optionally one warehouse), ordered FEFO with FIFO fallback (expiration ascending with null last, then received, then id ascending).

public getBatches(int $stockItemId[, int|null $warehouseId = null ]) : array<int, StockBatch>
Parameters
$stockItemId : int
$warehouseId : int|null = null
Return values
array<int, StockBatch>

getVacationNotice()

Returns active and upcoming vacations with an indicative delivery date after each ends.

public getVacationNotice([DateTimeInterface|null $date = null ][, int $daysBefore = 3 ]) : array<int, DateTimeImmutable}>

It is the single source of truth for the vacation banner.

A vacation is returned when its window runs today or starts within $daysBefore days. Each entry carries:

  • shipsFrom: end_date + 1, then chained through all further active vacations (a consecutive window moves the date past itself). It uses the same re-scan loop as self::resolveShippingDay(), so the banner and the algorithm cannot disagree.
  • deliveryDays: min(delivery_days) over active warehouses with vacation_affected = 1 (only those wait for the vacation to end; null when there is no such warehouse). It is global, not per item, because the banner is global.
  • deliveryEstimate: shipsFrom + deliveryDays (null when deliveryDays is null).

It ignores the stock availability of a particular item; that is the job of self::getAvailabilityBatch(). This is an indicative message for the whole website.

Parameters
$date : DateTimeInterface|null = null
$daysBefore : int = 3
Return values
array<int, DateTimeImmutable}> —

ordered by startDate ascending

resetMemo()

Drops the memoized vacation windows (called by `VacationManager` after a write or delete).

public resetMemo() : void

buildAvailability()

A deactivated stock item is withdrawn from sale: the physical totals and the warehouse rows stay as they are, but nothing is available, there is no delivery estimate and no label warehouse.

private buildAvailability(int $stockItemId, array<int, array<string, mixed>> $levelRows, int|null $supplierDeliveryDays, DateTimeImmutable $today, DateTimeImmutable $shiftedDay[, bool $itemActivated = true ]) : ItemAvailability
Parameters
$stockItemId : int
$levelRows : array<int, array<string, mixed>>
$supplierDeliveryDays : int|null
$today : DateTimeImmutable
$shiftedDay : DateTimeImmutable
$itemActivated : bool = true
Return values
ItemAvailability

chainPastWindows()

Moves `$day` past every window it falls into, re-scanning so that consecutive vacations chain (a shifted day may fall into the next window).

private chainPastWindows(DateTimeImmutable $day, array<int, DateTimeImmutable}> $windows) : DateTimeImmutable
Parameters
$day : DateTimeImmutable
$windows : array<int, DateTimeImmutable}>
Return values
DateTimeImmutable

daysBetween()

private daysBetween(DateTimeImmutable $from, DateTimeImmutable $to) : int
Parameters
$from : DateTimeImmutable
$to : DateTimeImmutable
Return values
int

decimal()

private decimal(mixed $value) : numeric-string
Parameters
$value : mixed
Return values
numeric-string

loadActiveVacationWindows()

Returns active vacation windows (one query). Shared by the delivery algorithm and by the banner helper {@see self::getVacationNotice()}, so there is a single definition of "active vacation" and the two cannot drift apart.

private loadActiveVacationWindows([bool $withId = false ]) : array<int, DateTimeImmutable}>
Parameters
$withId : bool = false

include vacation_id (banner needs to pair rows with entities)

Return values
array<int, DateTimeImmutable}>

resolveDelivery()

Resolves delivery days and the label warehouse. The vacation shift is computed per warehouse (`vacation_affected`) before `min()`, because a warehouse without the checkbox ships during the vacation too. The minimum is therefore taken over the resulting shipping dates, not over `delivery_days` (a warehouse with a longer lead time but no vacation can deliver sooner than a fast warehouse waiting for the vacation to end).

private resolveDelivery(array<int, array{order: int, warehouseId: int, deliveryDays: int, vacationAffected: bool}> $activeAvailable, int|null $supplierDeliveryDays, DateTimeImmutable $today, DateTimeImmutable $shiftedDay) : array{0: ?int, 1: ?int}
Parameters
$activeAvailable : array<int, array{order: int, warehouseId: int, deliveryDays: int, vacationAffected: bool}>
$supplierDeliveryDays : int|null
$today : DateTimeImmutable
$shiftedDay : DateTimeImmutable
Return values
array{0: ?int, 1: ?int} —

[deliveryDays, labelWarehouseId]

resolveShippingDay()

Shift today past any active vacation window(s), including consecutive ones.

private resolveShippingDay(array<int, DateTimeImmutable}> $windows) : DateTimeImmutable
Parameters
$windows : array<int, DateTimeImmutable}>
Return values
DateTimeImmutable

resolveVacationAffectedDeliveryDays()

Returns `min(delivery_days)` over active warehouses affected by the vacation (`vacation_affected = 1`). Null means no such warehouse exists (all have the checkbox off or are inactive), so the banner shows no date because no warehouse waits for the vacation to end.

private resolveVacationAffectedDeliveryDays() : int|null
Return values
int|null

toDay()

private toDay(mixed $value) : DateTimeImmutable|null
Parameters
$value : mixed
Return values
DateTimeImmutable|null

toInt()

private toInt(mixed $value) : int
Parameters
$value : mixed
Return values
int
On this page

Search results