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
13 changes: 13 additions & 0 deletions CHANGELOG-3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# CHANGELOG

### v3.0.0 (2025-10-24)

- [#397](https://github.com/Sylius/InvoicingPlugin/pull/397) Isolate plugin messaging: Introduce dedicated event & command buses for InvoicingPlugin ([@tomkalon](https://github.com/tomkalon))
- [#398](https://github.com/Sylius/InvoicingPlugin/pull/398) Persisted PDF path & file flow
- `Invoice`: added `path` field (UNIQUE).
- `InvoiceFactory`: inject `InvoiceFileNameGeneratorInterface`; use `generateForPdf($number)` to set `path` on creation.
- `InvoiceFileProvider`: removed dependency on file-name generator; added `%sylius_invoicing.pdf_generator.enabled%`; now relies on `Invoice::path()`.
- `InvoiceCreator`: removed `InvoicePdfFileGeneratorInterface` and `InvoiceFileManagerInterface`; PDF is no longer generated on invoice creation—it's generated lazily on first download/provide.
- `InvoiceFileNameGeneratorInterface::generateForPdf()` now accepts `string $invoiceNumber` instead of `InvoiceInterface`.
- `InvoiceFileNameGeneratorInterface::generateForPdf()` can prefix filenames based on `SYLIUS_INVOICING_SEQUENCE_SCOPE` (`global` – default, `monthly`, `annually`).
- `InvoicePdfFileGenerator`: removed `InvoiceFileNameGeneratorInterface` from constructor; filename is taken from `Invoice::path()`; update DI to drop the generator argument.
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
104 changes: 104 additions & 0 deletions UPGRADE-3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# UPGRADE FROM 2.2 TO 3.0

## Changes

1. Persisted PDF path on `Invoice`:

- Added to `Invoice` new `path` field (unique) storing the final PDF location (e.g. annually/2025_10_000000001.pdf).

2. Filename generation moved from `InvoiceCreator` to `InvoiceFactory`.

`InvoiceCreator` no longer generates PDFs at creation time.

PDFs are generated on first provide/download via the provider.

```xml
<service id="sylius_invoicing.custom_factory.invoice" class="Sylius\InvoicingPlugin\Factory\InvoiceFactory">
<argument>%sylius_invoicing.model.invoice.class%</argument>
<argument type="service" id="sylius_invoicing.factory.shop_billing_data" />
+ <argument type="service" id="sylius_invoicing.generator.invoice_file_name" />
</service>
```

```xml
<service id="sylius_invoicing.creator.invoice" class="Sylius\InvoicingPlugin\Creator\InvoiceCreator">
<argument type="service" id="sylius_invoicing.repository.invoice" />
<argument type="service" id="sylius.repository.order" />
<argument type="service" id="sylius_invoicing.generator.invoice" />
- <argument type="service" id="sylius_invoicing.generator.invoice_pdf_file" />
- <argument type="service" id="sylius_invoicing.manager.invoice_file" />
- <argument>%sylius_invoicing.pdf_generator.enabled%</argument>
</service>
```

3. `InvoiceFactory` now depends on `InvoiceFileNameGeneratorInterface`.
```xml
<service id="sylius_invoicing.custom_factory.invoice" class="Sylius\InvoicingPlugin\Factory\InvoiceFactory">
<argument>%sylius_invoicing.model.invoice.class%</argument>
<argument type="service" id="sylius_invoicing.factory.shop_billing_data" />
+ <argument type="service" id="sylius_invoicing.generator.invoice_file_name" />
</service>
```

On creation, it calls:
```php
$fileName = $invoiceFileNameGenerator->generateForPdf($number);
```
and passes it to the `Invoice` constructor as `$path`.

4. `InvoiceFileProvider` is now the primary orchestrator of PDF generation

Removed `InvoiceFileNameGeneratorInterface` from `InvoiceFileProvider`.

Added `sylius_invoicing.pdf_generator.enabled` parameter to constructor.

```xml
<service id="sylius_invoicing.provider.invoice_file" class="Sylius\InvoicingPlugin\Provider\InvoiceFileProvider">
- <argument type="service" id="sylius_invoicing.generator.invoice_file_name" />
<argument type="service" id="gaufrette.sylius_invoicing_invoice_filesystem" />
<argument type="service" id="sylius_invoicing.generator.invoice_pdf_file" />
<argument type="service" id="sylius_invoicing.manager.invoice_file" />
<argument>%sylius_invoicing.invoice_save_path%</argument>
+ <argument>%sylius_invoicing.pdf_generator.enabled%</argument>
</service>
```

5. `InvoiceFileNameGenerator` signature & scoping

BC break: `generateForPdf()` now accepts string $invoiceNumber (not `InvoiceInterface`).

```php
// before:
public function generateForPdf(InvoiceInterface $invoice): string;

// after:
public function generateForPdf(string $invoiceNumber): string;
```

6. Can prefix filenames based on `SYLIUS_INVOICING_SEQUENCE_SCOPE`:

>global (default): no prefix
>
>monthly: monthly/…
>
>annually: annually/…

7. `InvoicePdfFileGenerator` simplified:

- Removed dependency on InvoiceFileNameGeneratorInterface.

- Uses `Invoice::path()` as the filename:

```xml
<service id="sylius_invoicing.generator.invoice_pdf_file" class="Sylius\InvoicingPlugin\Generator\InvoicePdfFileGenerator">
<argument type="service" id="sylius_invoicing.generator.twig_to_pdf" />
<argument type="service" id="file_locator" />
- <argument type="service" id="sylius_invoicing.generator.invoice_file_name" />
<argument>@SyliusInvoicingPlugin/shared/download/pdf.html.twig</argument>
<argument>%sylius_invoicing.template.logo_file%</argument>
</service>
```

```php
$filename = $invoice->path();
```
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'

sylius_invoicing:
pdf_generator:
Expand Down
1 change: 1 addition & 0 deletions config/doctrine/Invoice.orm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<field name="localeCode" column="locale_code" />
<field name="total" column="total" type="integer" />
<field name="paymentState" column="payment_state" />
<field name="path" column="path" unique="true" />

<one-to-one field="billingData" target-entity="Sylius\InvoicingPlugin\Entity\BillingDataInterface">
<cascade>
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>
3 changes: 2 additions & 1 deletion config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<service id="sylius_invoicing.custom_factory.invoice" class="Sylius\InvoicingPlugin\Factory\InvoiceFactory">
<argument>%sylius_invoicing.model.invoice.class%</argument>
<argument type="service" id="sylius_invoicing.factory.shop_billing_data" />
<argument type="service" id="sylius_invoicing.generator.invoice_file_name" />
</service>
<service id="Sylius\InvoicingPlugin\Factory\InvoiceFactoryInterface" alias="sylius_invoicing.custom_factory.invoice" />

Expand All @@ -60,11 +61,11 @@
<service id="Sylius\InvoicingPlugin\Manager\InvoiceFileManagerInterface" alias="sylius_invoicing.manager.invoice_file" />

<service id="sylius_invoicing.provider.invoice_file" class="Sylius\InvoicingPlugin\Provider\InvoiceFileProvider">
<argument type="service" id="sylius_invoicing.generator.invoice_file_name" />
<argument type="service" id="gaufrette.sylius_invoicing_invoice_filesystem" />
<argument type="service" id="sylius_invoicing.generator.invoice_pdf_file" />
<argument type="service" id="sylius_invoicing.manager.invoice_file" />
<argument>%sylius_invoicing.invoice_save_path%</argument>
<argument>%sylius_invoicing.pdf_generator.enabled%</argument>
</service>
<service id="Sylius\InvoicingPlugin\Provider\InvoiceFileProviderInterface" alias="sylius_invoicing.provider.invoice_file" />

Expand Down
12 changes: 4 additions & 8 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 All @@ -38,16 +39,14 @@
</service>
<service id="Sylius\InvoicingPlugin\Generator\InvoiceGeneratorInterface" alias="sylius_invoicing.generator.invoice" />

<service
id="sylius_invoicing.generator.invoice_file_name"
class="Sylius\InvoicingPlugin\Generator\InvoiceFileNameGenerator"
/>
<service id="sylius_invoicing.generator.invoice_file_name" class="Sylius\InvoicingPlugin\Generator\InvoiceFileNameGenerator">
<argument>%sylius_invoicing.sequence_scope%</argument>
</service>
<service id="Sylius\InvoicingPlugin\Generator\InvoiceFileNameGeneratorInterface" alias="sylius_invoicing.generator.invoice_file_name" />

<service id="sylius_invoicing.generator.invoice_pdf_file" class="Sylius\InvoicingPlugin\Generator\InvoicePdfFileGenerator">
<argument type="service" id="sylius_invoicing.generator.twig_to_pdf" />
<argument type="service" id="file_locator" />
<argument type="service" id="sylius_invoicing.generator.invoice_file_name" />
<argument>@SyliusInvoicingPlugin/shared/download/pdf.html.twig</argument>
<argument>%sylius_invoicing.template.logo_file%</argument>
</service>
Expand All @@ -57,9 +56,6 @@
<argument type="service" id="sylius_invoicing.repository.invoice" />
<argument type="service" id="sylius.repository.order" />
<argument type="service" id="sylius_invoicing.generator.invoice" />
<argument type="service" id="sylius_invoicing.generator.invoice_pdf_file" />
<argument type="service" id="sylius_invoicing.manager.invoice_file" />
<argument>%sylius_invoicing.pdf_generator.enabled%</argument>
</service>
<service id="Sylius\InvoicingPlugin\Creator\InvoiceCreatorInterface" alias="sylius_invoicing.creator.invoice" />

Expand Down
21 changes: 1 addition & 20 deletions src/Creator/InvoiceCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,19 @@

namespace Sylius\InvoicingPlugin\Creator;

use Doctrine\ORM\Exception\ORMException;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\InvoicingPlugin\Doctrine\ORM\InvoiceRepositoryInterface;
use Sylius\InvoicingPlugin\Entity\InvoiceInterface;
use Sylius\InvoicingPlugin\Exception\InvoiceAlreadyGenerated;
use Sylius\InvoicingPlugin\Generator\InvoiceGeneratorInterface;
use Sylius\InvoicingPlugin\Generator\InvoicePdfFileGeneratorInterface;
use Sylius\InvoicingPlugin\Manager\InvoiceFileManagerInterface;

final class InvoiceCreator implements InvoiceCreatorInterface
{
public function __construct(
private readonly InvoiceRepositoryInterface $invoiceRepository,
private readonly OrderRepositoryInterface $orderRepository,
private readonly InvoiceGeneratorInterface $invoiceGenerator,
private readonly InvoicePdfFileGeneratorInterface $invoicePdfFileGenerator,
private readonly InvoiceFileManagerInterface $invoiceFileManager,
private readonly bool $hasEnabledPdfFileGenerator = true,
) {
}

Expand All @@ -49,19 +43,6 @@ public function __invoke(string $orderNumber, \DateTimeInterface $dateTime): voi

$invoice = $this->invoiceGenerator->generateForOrder($order, $dateTime);

if (!$this->hasEnabledPdfFileGenerator) {
$this->invoiceRepository->add($invoice);

return;
}

$invoicePdf = $this->invoicePdfFileGenerator->generate($invoice);
$this->invoiceFileManager->save($invoicePdf);

try {
$this->invoiceRepository->add($invoice);
} catch (ORMException) {
$this->invoiceFileManager->remove($invoicePdf);
}
$this->invoiceRepository->add($invoice);
}
}
6 changes: 6 additions & 0 deletions src/Entity/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public function __construct(
protected ChannelInterface $channel,
protected string $paymentState,
protected InvoiceShopBillingDataInterface $shopBillingData,
protected string $path,
) {
$this->issuedAt = clone $issuedAt;

Expand Down Expand Up @@ -143,4 +144,9 @@ public function paymentState(): string
{
return $this->paymentState;
}

public function path(): string
{
return $this->path;
}
}
2 changes: 2 additions & 0 deletions src/Entity/InvoiceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ public function channel(): ChannelInterface;
public function shopBillingData(): InvoiceShopBillingDataInterface;

public function paymentState(): string;

public function path(): string;
}
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;
}
Loading