Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/var/
/vendor/
/node_modules/
/composer.lock
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG-2.0.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

### v2.0.3 (2025-10-21)

- [#373](https://github.com/Sylius/InvoicingPlugin/pull/373) Add configurable invoice sequence scope (`monthly`/`annually`/`global`)
via SYLIUS_INVOICING_SEQUENCE_SCOPE ENV ([@tomkalon](https://github.com/tomkalon))

### v2.0.2 (2025-07-03)

- [#373](https://github.com/Sylius/InvoicingPlugin/pull/373) Add sylius/test-application ([@Wojdylak](https://github.com/Wojdylak))
Expand Down
14 changes: 14 additions & 0 deletions UPGRADE-2.0.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# UPGRADE FROM 2.0 TO 2.1

## Changes

1. Added support for configurable invoice sequence scoping via the SYLIUS_INVOICING_SEQUENCE_SCOPE environment variable:

- monthly: resets invoice numbering each month
- annually: resets invoice numbering each year
- global or unset (default): uses a single global sequence (as previously)

## Deprecations

1. Not passing the $scope argument (of type InvoiceSequenceScopeEnum) to the constructor of SequentialInvoiceNumberGenerator is deprecated and will be required starting from version 3.0.

# UPGRADE FROM 1.X TO 2.0

1. Support for Sylius 2.0 has been added, it is now the recommended Sylius version to use with InvoicingPlugin.
Expand Down
2 changes: 2 additions & 0 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ imports:
parameters:
sylius_invoicing.invoice_save_path: "%kernel.project_dir%/private/invoices/"
sylius_invoicing.filesystem_adapter.invoice: "sylius_invoicing_invoice"
sylius_invoicing.sequence_scope: '%env(default::SYLIUS_INVOICING_SEQUENCE_SCOPE)%'
env(SYLIUS_INVOICING_SEQUENCE_SCOPE): 'global'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with the (anything else or empty): one global sequence.

Either it's global or empty, but "anything else" could lead to future issues with custom developments.


sylius_invoicing:
pdf_generator:
Expand Down
3 changes: 3 additions & 0 deletions config/doctrine/InvoiceSequence.orm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
</id>
<field name="index" column="idx" type="integer" />
<field name="version" type="integer" version="true" />
<field name="year" type="integer" nullable="true"/>
<field name="month" type="integer" nullable="true"/>
<field name="type" enum-type="Sylius\InvoicingPlugin\Enum\InvoiceSequenceScopeEnum" nullable="true" />
</mapped-superclass>

</doctrine-mapping>
1 change: 1 addition & 0 deletions config/services/generators.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<argument type="service" id="sylius_invoicing.factory.invoice_sequence" />
<argument type="service" id="sylius_invoicing.manager.invoice_sequence" />
<argument type="service" id="clock" />
<argument key="$scope">%sylius_invoicing.sequence_scope%</argument>
</service>

<service id="sylius_invoicing.generator.invoice_identifier" class="Sylius\InvoicingPlugin\Generator\UuidInvoiceIdentifierGenerator" />
Expand Down
38 changes: 38 additions & 0 deletions src/Entity/InvoiceSequence.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

namespace Sylius\InvoicingPlugin\Entity;

use Sylius\InvoicingPlugin\Enum\InvoiceSequenceScopeEnum;

/** @final */
class InvoiceSequence implements InvoiceSequenceInterface
{
Expand All @@ -23,6 +25,12 @@ class InvoiceSequence implements InvoiceSequenceInterface

protected ?int $version = 1;

protected ?InvoiceSequenceScopeEnum $type = null;

protected ?int $year = null;

protected ?int $month = null;

/** @return mixed */
public function getId()
{
Expand All @@ -48,4 +56,34 @@ public function setVersion(?int $version): void
{
$this->version = $version;
}

public function getType(): ?InvoiceSequenceScopeEnum
{
return $this->type;
}

public function setType(?InvoiceSequenceScopeEnum $type): void
{
$this->type = $type;
}

public function getYear(): ?int
{
return $this->year;
}

public function setYear(?int $year): void
{
$this->year = $year;
}

public function getMonth(): ?int
{
return $this->month;
}

public function setMonth(?int $month): void
{
$this->month = $month;
}
}
13 changes: 13 additions & 0 deletions src/Entity/InvoiceSequenceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,23 @@

use Sylius\Component\Resource\Model\ResourceInterface;
use Sylius\Component\Resource\Model\VersionedInterface;
use Sylius\InvoicingPlugin\Enum\InvoiceSequenceScopeEnum;

interface InvoiceSequenceInterface extends ResourceInterface, VersionedInterface
{
public function getIndex(): int;

public function incrementIndex(): void;

public function getType(): ?InvoiceSequenceScopeEnum;

public function setType(?InvoiceSequenceScopeEnum $type): void;

public function getYear(): ?int;

public function getMonth(): ?int;

public function setYear(?int $year): void;

public function setMonth(?int $month): void;
}
21 changes: 21 additions & 0 deletions src/Enum/InvoiceSequenceScopeEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\InvoicingPlugin\Enum;

enum InvoiceSequenceScopeEnum: string
{
case GLOBAL = 'global';
case MONTHLY = 'monthly';
case ANNUALLY = 'annually';
}
51 changes: 47 additions & 4 deletions src/Generator/SequentialInvoiceNumberGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Sylius\InvoicingPlugin\Entity\InvoiceSequenceInterface;
use Sylius\InvoicingPlugin\Enum\InvoiceSequenceScopeEnum;
use Symfony\Component\Clock\ClockInterface;

final class SequentialInvoiceNumberGenerator implements InvoiceNumberGenerator
Expand All @@ -29,7 +30,17 @@ public function __construct(
private readonly ClockInterface $clock,
private readonly int $startNumber = 1,
private readonly int $numberLength = 9,
private readonly ?string $scope = null,
) {
if (null === $this->scope) {
trigger_deprecation(
'sylius/invoicing-plugin',
'2.1',
'Not passing the "%s" argument to "%s::__construct()" is deprecated and will be required in version 3.0. Pass a valid scope explicitly (e.g. "monthly", "annually", or "global").',
'scope',
self::class,
);
}
}

public function generate(): string
Expand All @@ -56,15 +67,47 @@ private function generateNumber(int $index): string

private function getSequence(): InvoiceSequenceInterface
{
/** @var InvoiceSequenceInterface $sequence */
$sequence = $this->sequenceRepository->findOneBy([]);

if (null != $sequence) {
$now = $this->clock->now();
$scope = InvoiceSequenceScopeEnum::tryFrom($this->scope ?? '') ?? InvoiceSequenceScopeEnum::GLOBAL;

$criteria = match ($scope) {
InvoiceSequenceScopeEnum::MONTHLY => [
'year' => (int) $now->format('Y'),
'month' => (int) $now->format('m'),
Comment on lines +75 to +76

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why you have values here.

You wrote "monthly" or "yearly". You didn't specify that we could chose when it happens precisely.
I feel strange about this.

'type' => $scope,
],
InvoiceSequenceScopeEnum::ANNUALLY => [
'year' => (int) $now->format('Y'),
'type' => $scope,
],
InvoiceSequenceScopeEnum::GLOBAL => [
'year' => null,
'month' => null,
],
};

/** @var InvoiceSequenceInterface|null $sequence */
$sequence = $this->sequenceRepository->findOneBy($criteria);

if (null !== $sequence) {
return $sequence;
}

/** @var InvoiceSequenceInterface $sequence */
$sequence = $this->sequenceFactory->createNew();

if (isset($criteria['year'])) {
$sequence->setYear($criteria['year']);
}

if (isset($criteria['month'])) {
$sequence->setMonth($criteria['month']);
}

if (isset($criteria['type'])) {
$sequence->setType($criteria['type']);
}

$this->sequenceManager->persist($sequence);

return $sequence;
Expand Down
35 changes: 35 additions & 0 deletions src/Migrations/Version20251021074051.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\InvoicingPlugin\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20251021074051 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add year, month and type columns to sylius_invoicing_plugin_sequence table';
}

public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE sylius_invoicing_plugin_sequence ADD year INT DEFAULT NULL, ADD month INT DEFAULT NULL, ADD type VARCHAR(255) DEFAULT NULL');
}

public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE sylius_invoicing_plugin_sequence DROP year, DROP month, DROP type');
}
}
3 changes: 3 additions & 0 deletions tests/TestApplication/.env
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ WKHTMLTOPDF_PATH=/usr/local/bin/wkhtmltopdf
###< knplabs/knp-snappy-bundle ###

TEST_SYLIUS_INVOICING_PDF_GENERATION_DISABLED=false
TEST_SYLIUS_INVOICING_PDF_GENERATION_DISABLED=false

SYLIUS_INVOICING_SEQUENCE_SCOPE='monthly'
Loading