InvoiceNumberGenerator
in package
Generates an invoice number from a billing line (`BillingLine`).
Called by InvoiceManager::beforeSave() on INSERT of an invoice without a manually entered number. From the line it derives:
periodKey: sequence period key by the linebilling_period(see resolvePeriodKey()),order: sequence number within (line, period) = max(order) + 1,number: line mask with%Y/%m/%d(from the issue date) and%number(zero-padded tonumber_length),variableSymbol: digits of the number only, unless the VS was entered manually.
IMPORTANT: the critical section is serialized by SELECT ... FOR UPDATE on the billing line row (an existing row
is a reliable lock across transactions, held until the save transaction ends). The schema backstop is
UNIQUE(billing_line_id, order, period_key); a duplicate key would fail the transaction, the lock prevents it.
The generator runs inside the already open save transaction (SaveTrait::save()
begins it before beforeSave; TransactionManager shares the same EntityManagerInterface).
IMPORTANT: raw DBAL on purpose. It never touches the Doctrine identity map (no manager->find()/findAll()):
those end in GetAllTrait::clearDoctrine(), which detaches the freshly hydrated invoice object graph before
flush and loses it. The line and max(order) are therefore read through getConnection() only.
Table of Contents
Constants
- BILLING_LINE_TABLE : string = 'cms_mod_invoicer_billing_lines'
- INVOICE_TABLE : string = 'cms_mod_invoicer_invoices'
Properties
- $em : EntityManagerInterface
Methods
- __construct() : mixed
- generate() : bool
- Generates and sets `order`, `periodKey`, `number` (and fills `variableSymbol`) on the invoice.
- numberExists() : bool
- Tells whether an invoice with this number already exists.
- buildNumber() : string
- Builds the invoice number from the line mask: date placeholders (`%Y`/`%m`/`%d`) from the issue date, `%number` = sequence number zero-padded to `numberLength`.
- deriveVariableSymbol() : string|null
- Derives the variable symbol: digits of the invoice number only (the mask may carry a prefix or separators, a VS is purely numeric). An empty result gives null (the VS stays empty).
- resolvePeriodKey() : string
- Returns the period key of the number sequence by the line period: - `never` gives `''` (one endless sequence), - `year` gives `'2026'`, - `month` gives `'2026-07'`, - `day` gives `'2026-07-06'`.
Constants
BILLING_LINE_TABLE
private
string
BILLING_LINE_TABLE
= 'cms_mod_invoicer_billing_lines'
INVOICE_TABLE
private
string
INVOICE_TABLE
= 'cms_mod_invoicer_invoices'
Properties
$em read-only
private
EntityManagerInterface
$em
Methods
__construct()
public
__construct(EntityManagerInterface $em) : mixed
Parameters
- $em : EntityManagerInterface
generate()
Generates and sets `order`, `periodKey`, `number` (and fills `variableSymbol`) on the invoice.
public
generate(Invoice $invoice) : bool
Returns false when the line does not exist (it vanished between the form selection and saving); the caller
(InvoiceManager::beforeSave()) then fails the save with an error instead of storing a silent invoice
without a number. Without a line the generator is not called at all (the manager rejects it earlier).
Parameters
- $invoice : Invoice
Return values
boolnumberExists()
Tells whether an invoice with this number already exists.
public
numberExists(string $number[, int|null $excludeInvoiceId = null ]) : bool
number has no unique index (the only unique one is the sequence uq_invoice_sequence on
order/period_key/line), so the application guards duplicates: InvoiceManager::beforeSave() rejects an
insert with an existing manual number and a generated number colliding with a manual import. Raw DBAL for the
same reason as generate().
Parameters
- $number : string
- $excludeInvoiceId : int|null = null
Return values
boolbuildNumber()
Builds the invoice number from the line mask: date placeholders (`%Y`/`%m`/`%d`) from the issue date, `%number` = sequence number zero-padded to `numberLength`.
private
buildNumber(string $mask, DateTime $issuedAt, int $order, int $numberLength) : string
Parameters
- $mask : string
- $issuedAt : DateTime
- $order : int
- $numberLength : int
Return values
stringderiveVariableSymbol()
Derives the variable symbol: digits of the invoice number only (the mask may carry a prefix or separators, a VS is purely numeric). An empty result gives null (the VS stays empty).
private
deriveVariableSymbol(string $number) : string|null
Parameters
- $number : string
Return values
string|nullresolvePeriodKey()
Returns the period key of the number sequence by the line period: - `never` gives `''` (one endless sequence), - `year` gives `'2026'`, - `month` gives `'2026-07'`, - `day` gives `'2026-07-06'`.
private
resolvePeriodKey(string $billingPeriod, DateTime $issuedAt) : string
Parameters
- $billingPeriod : string
- $issuedAt : DateTime