Skip to content

[2.3][Feature] Add support for configurable invoice sequence scopes #435

Open
tomkalon wants to merge 7 commits into
Sylius:2.3from
tomkalon:feature/sequence-number
Open

[2.3][Feature] Add support for configurable invoice sequence scopes #435
tomkalon wants to merge 7 commits into
Sylius:2.3from
tomkalon:feature/sequence-number

Conversation

@tomkalon

@tomkalon tomkalon commented Jul 17, 2026

Copy link
Copy Markdown
Q A
Branch? 2.3
Bug fix? no
New feature? yes
BC breaks? yes
Related tickets #131
License MIT

Summary

Adds configurable invoice number sequence scoping (global / monthly / annually, defaulting to global for full backward compatibility) and closes a race condition that could produce duplicated invoice numbers.

  • New SequenceScopeResolverInterface with Global/Monthly/Annually resolvers, pluggable via sylius_invoicing.sequence_scope_resolver tag.
  • Unique DB constraints: one invoice number across the whole system, one sequence counter per scope period.
  • InvoiceCreator now persists the invoice before writing its PDF, so a duplicate number fails on the DB constraint instead of silently overwriting a file.
  • Migrations provided for both MySQL and PostgreSQL.
  • Behat coverage for all three scopes, duplicate prevention, and invoice file recovery.

Config

New sequence.scope option under sylius_invoicing, defaults to global:

sylius_invoicing:
    sequence:
        scope: global # one of "global", "monthly", "annually"
  • global — one ever-increasing counter for the whole store, never reset (previous behavior).
  • monthly — a counter per year/month, reset on the 1st of every month.
  • annually — a counter per year, reset on the 1st of January.

All three share the same Y/m/index number format — only the reset behavior differs.

Custom scopes can be added by registering a service implementing SequenceScopeResolverInterface,
tagged with sylius_invoicing.sequence_scope_resolver, and setting its name as scope.

BC breaks

Full details in UPGRADE-2.3.md.

  • InvoiceSequenceInterface gained getType()/setType(), getYear()/setYear(), getMonth()/setMonth()
    and the SCOPE_GLOBAL/SCOPE_MONTHLY/SCOPE_ANNUALLY constants. Any custom class implementing this interface
    directly (rather than extending the provided InvoiceSequence) will fail to compile until it adds these methods.

  • InvoiceCreator now persists the invoice before writing its PDF file (previously the other way round), and
    no longer catches ORMException around the persist call. A duplicated invoice number now fails loudly with a DB
    constraint violation propagated to the caller, instead of being silently swallowed while the PDF file was removed.
    Callers relying on createInvoice() never throwing must be updated to handle the exception.

  • SequentialInvoiceNumberGenerator constructor gained two new optional arguments ($scopeResolvers, $scope).
    Not a breaking change — omitting $scopeResolvers triggers a deprecation and falls back to the built-in resolvers.

    Add support for configurable invoice sequence scopes #429

@tomkalon
tomkalon marked this pull request as draft July 17, 2026 13:34
@tomkalon
tomkalon force-pushed the feature/sequence-number branch 2 times, most recently from 153dc69 to 9431981 Compare July 20, 2026 07:23
@tomkalon
tomkalon force-pushed the feature/sequence-number branch from 9431981 to 1d6ed4c Compare July 20, 2026 07:43
@tomkalon
tomkalon marked this pull request as ready for review July 20, 2026 07:47
@tomkalon
tomkalon force-pushed the feature/sequence-number branch from 1d6ed4c to fb2712a Compare July 20, 2026 07:51
@tomkalon
tomkalon marked this pull request as draft July 20, 2026 10:19
@tomkalon
tomkalon marked this pull request as ready for review July 20, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant