MarginReporter
in package
Read-only margin report.
Revenue, COGS and margin come only from posted (status=1) issue documents (type=2)
that are not storno counter-documents (source_type <> 'storno'); without this filter
transfers (also -q with a price) and storno pairs would double or skew the numbers. A
separate inventory-differences line aggregates posted inventory corrections (type=4): the
signed quantity SUM(q) and its value (shortages negative, surpluses positive), with the same
storno exclusion: a cancelled inventory contributes nothing. The value uses the price each
movement carries in DocumentService::adjust(): a shortage has the FEFO cogs_unit_price and no
unit_price_base, a surplus has the batch price in unit_price_base and no cogs_unit_price.
Uses raw DBAL with the same read-only shape as
StockBatchManager::watchExpiry() and
StockRecalculator: no ORM hydration, no writes. The period filters on
stock_documents.posted_datetime (the accounting date), the same column the admin
StockDocument grid range-filters on.
Table of Contents
Constants
- DOCUMENTS : string = 'cms_mod_store_stock_documents'
- MOVEMENTS : string = 'cms_mod_store_stock_movements'
Properties
- $em : EntityManagerInterface
Methods
- __construct() : mixed
- report() : array{from: string, to: string, stockItemId: ?int, movementCount: int, revenue: string, cogs: string, margin: string, inventory: array{movementCount: int, quantity: string, cogsValue: string}}
- aggregate() : array{0: int, 1: string, 2: string}
- Issue documents: [movementCount, SUM(-q * unit_price_base), SUM(-q * cogs_unit_price)].
- n() : numeric-string
- rInt() : int
- rStr() : string
Constants
DOCUMENTS
private
string
DOCUMENTS
= 'cms_mod_store_stock_documents'
MOVEMENTS
private
string
MOVEMENTS
= 'cms_mod_store_stock_movements'
Properties
$em read-only
private
EntityManagerInterface
$em
Methods
__construct()
public
__construct(EntityManagerInterface $em) : mixed
Parameters
- $em : EntityManagerInterface
report()
public
report(DateTimeInterface $from, DateTimeInterface $to[, int|null $stockItemId = null ]) : array{from: string, to: string, stockItemId: ?int, movementCount: int, revenue: string, cogs: string, margin: string, inventory: array{movementCount: int, quantity: string, cogsValue: string}}
Parameters
- $from : DateTimeInterface
- $to : DateTimeInterface
- $stockItemId : int|null = null
Return values
array{from: string, to: string, stockItemId: ?int, movementCount: int, revenue: string, cogs: string, margin: string, inventory: array{movementCount: int, quantity: string, cogsValue: string}}aggregate()
Issue documents: [movementCount, SUM(-q * unit_price_base), SUM(-q * cogs_unit_price)].
private
aggregate(int $documentType, string $fromStr, string $toStr, int|null $stockItemId, bool $issueSemantics) : array{0: int, 1: string, 2: string}
Inventory documents: [movementCount, SUM(q), SUM(q * COALESCE(cogs_unit_price, unit_price_base))].
IMPORTANT: inventory movements carry their price in one of two columns (see the class doc).
Multiplying the quantity by unit_price_base turned the quantity slot into the value of the
surpluses (shortages are NULL there and dropped out), and q * cogs_unit_price alone left the
surpluses out of the value.
Parameters
- $documentType : int
- $fromStr : string
- $toStr : string
- $stockItemId : int|null
- $issueSemantics : bool
Return values
array{0: int, 1: string, 2: string}n()
private
n(string $value) : numeric-string
Parameters
- $value : string
Return values
numeric-string —bcmath wrapper (same as DocumentService::n()).
rInt()
private
rInt(mixed $value) : int
Parameters
- $value : mixed
Return values
intrStr()
private
rStr(mixed $value) : string
Parameters
- $value : mixed