mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
Merge branch '1.9' into 1.10
* 1.9: Update docs/cookbook/invoices/generating-invoice-after-payment.rst Fixes to lists [DOCS] Generating invoice after payment Fix to ordered list [API] Fix formating for list in invoice cookbook [DOCS] How to modify invoices'
This commit is contained in:
commit
8e0898ee54
9 changed files with 388 additions and 0 deletions
BIN
docs/_images/cookbook/custom-invoice/pdf_after.png
Normal file
BIN
docs/_images/cookbook/custom-invoice/pdf_after.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
BIN
docs/_images/cookbook/custom-invoice/pdf_before.png
Normal file
BIN
docs/_images/cookbook/custom-invoice/pdf_before.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
BIN
docs/_images/cookbook/custom-invoice/pdf_phone.png
Normal file
BIN
docs/_images/cookbook/custom-invoice/pdf_phone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/_images/cookbook/custom-invoice/pdf_total.png
Normal file
BIN
docs/_images/cookbook/custom-invoice/pdf_total.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -84,6 +84,17 @@ Inventory
|
|||
|
||||
.. include:: /cookbook/inventory/map.rst.inc
|
||||
|
||||
Invoices
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
invoices/custom-invoice
|
||||
invoices/generating-invoice-after-payment
|
||||
|
||||
.. include:: /cookbook/invoices/map.rst.inc
|
||||
|
||||
Shipping methods
|
||||
----------------
|
||||
|
||||
|
|
|
|||
205
docs/cookbook/invoices/custom-invoice.rst
Normal file
205
docs/cookbook/invoices/custom-invoice.rst
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
How to customize the order invoice?
|
||||
===================================
|
||||
|
||||
.. note::
|
||||
|
||||
This cookbook describes customization of a feature available only with `Sylius/InvoicingPlugin <https://github.com/Sylius/InvoicingPlugin/>`_ installed.
|
||||
|
||||
The invoicing plugin lets you generate invoice on admin panel and download PDF with it. This plugin also sends an email with this invoice
|
||||
as well as let the administrator to delegate an email resend.
|
||||
|
||||
Why would you customize the invoice?
|
||||
------------------------------------
|
||||
|
||||
Invoicing Plugin provides a generic solution for generating invoices for orders, it is enough for a basic invoicing functionality
|
||||
but many shops need this feature customized for its needs.
|
||||
For example, one may need to change the look of it, or add some more data.
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
Before you start make sure that you have:
|
||||
|
||||
#. `Sylius/InvoicingPlugin <https://github.com/Sylius/InvoicingPlugin/>`_ installed.
|
||||
#. `Wkhtmltopdf <https://wkhtmltopdf.org/>`_ package installed, because most of pdf generation is done with it.
|
||||
|
||||
How to customize the invoice appearance?
|
||||
----------------------------------------
|
||||
|
||||
There might be need to change how the invoices look like, f.e. shop is using its own logo, there might be some colours changed
|
||||
on the tables, or maybe the order of fields might be changed.
|
||||
|
||||
First let's prepare the HTML's so we can modify them. This command will copy only the PDF template:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir templates/bundles/SyliusInvoicingPlugin/Invoice/Download
|
||||
cp vendor/sylius/invoicing-plugin/src/Resources/views/Invoice/Download/pdf.html.twig templates/bundles/SyliusInvoicingPlugin/Download
|
||||
|
||||
But you can also copy all of the templates with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir templates/bundles/SyliusInvoicingPlugin
|
||||
cp -r vendor/sylius/invoicing-plugin/src/Resources/views/Invoice templates/bundles/SyliusInvoicingPlugin/
|
||||
|
||||
In directory ``templates/bundles/SyliusInvoicingPlugin/Invoice`` you can find now few files.
|
||||
|
||||
Let's modify the generated PDF. In this case we will modify the file from ``Download/pdf.html.twig``.
|
||||
This is how the default PDF looks like (don't worry this is not a real address of Myrna, or is it?):
|
||||
|
||||
.. image:: ../../_images/cookbook/custom-invoice/pdf_before.png
|
||||
|
||||
Now with some magic of HTML and CSS we can modify this template, as an example we can change the color of background to ``red`` by changing
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!--...-->
|
||||
<div class="invoice-box" style="background-color: red">
|
||||
<!--...-->
|
||||
|
||||
and after this change we are graced with this masterpiece:
|
||||
|
||||
.. image:: ../../_images/cookbook/custom-invoice/pdf_after.png
|
||||
|
||||
.. warning::
|
||||
|
||||
Every PDF that you generate is stored and then extracted so it won't be created again. If you want to see the changes
|
||||
go to ``private/invoices`` and remove the generated PDF. You should see the changes of your file when you generate it again.
|
||||
|
||||
.. note::
|
||||
|
||||
You can also modify the view on administrator page by changing code inside ``show.html.twig`` and related templates
|
||||
|
||||
.. note::
|
||||
|
||||
You can learn more about customizing templates at :doc:`Customization Guide </customization/index>`
|
||||
|
||||
How to add additional fields to invoice?
|
||||
----------------------------------------
|
||||
|
||||
Let's say that you need (or not) some more fields. In this example we will add the customer phone number.
|
||||
Because we are basing upon the existing field, there should be no problem adding it to document - just place a line into
|
||||
``Download/pdf.html.twig`` file. The ``Phone Number`` field is quite nested so you need to add ``invoice.order.customer.phoneNumber``
|
||||
to retrieve it:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!--...-->
|
||||
{{ invoice.billingData.city }}<br/>
|
||||
{{ invoice.order.customer.phoneNumber }}<br/>
|
||||
{{ invoice.billingData.countryCode}}
|
||||
<!--...-->
|
||||
|
||||
And as a result we can see that phone number has been added just after the city:
|
||||
|
||||
.. image:: ../../_images/cookbook/custom-invoice/pdf_phone.png
|
||||
|
||||
.. note::
|
||||
|
||||
You can also create some validation (for example if customer has no phone number) so the field won't be shown.
|
||||
If you want to learn more about twig - visit `twig <https://twig.symfony.com/>`_.
|
||||
|
||||
How to change the appearance of invoice tables?
|
||||
-----------------------------------------------
|
||||
|
||||
By default on lower right corner of invoice we are displaying ``total`` of ordered items and shipment.
|
||||
Lets create now a new row where we will show ``Products total`` where only price for products will be shown.
|
||||
|
||||
First let's add the new table row between other ``totals`` in ``pdf.html.twig``
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!--...-->
|
||||
<tr class="totals">
|
||||
<!--tr body-->
|
||||
</tr>
|
||||
|
||||
<tr class="totals">
|
||||
<td colspan="5"></td>
|
||||
<td colspan="2" >{{ 'sylius_invoicing_plugin.ui.products_total'|trans([], 'messages', invoice.localeCode) }}:</td>
|
||||
<td>{{ '%0.2f'|format(invoice.order.itemsTotal/100) }}</td>
|
||||
<td>{{ invoice.currencyCode }}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="totals bold">
|
||||
<!--...-->
|
||||
|
||||
And now add the translation by creating file ``translations/messages.en.yaml`` and adding:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
sylius_invoicing_plugin:
|
||||
ui:
|
||||
products_total: 'Products total'
|
||||
|
||||
after this changes your PDF's total table should look like this:
|
||||
|
||||
.. image:: ../../_images/cookbook/custom-invoice/pdf_total.png
|
||||
|
||||
How to extend Invoice with custom logic?
|
||||
----------------------------------------
|
||||
|
||||
With default behavior and some simple customization it should be quite simple to achieve the Invoice you are looking for.
|
||||
But life is not so straightforward as we all would like, and you are in need to create some custom logic for your needs.
|
||||
Scary process isn't it? Well not exactly, let's create some custom logic for your invoice in this step.
|
||||
|
||||
First we need a class with our logic that will extend current Invoice:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity\Invoice;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Sylius\InvoicingPlugin\Entity\Invoice as BaseInvoice;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="sylius_invoicing_plugin_invoice")
|
||||
*/
|
||||
class Invoice extends BaseInvoice implements InvoiceInterface
|
||||
{
|
||||
public function customFunction(): mixed
|
||||
{
|
||||
/** your custom logic */
|
||||
}
|
||||
}
|
||||
|
||||
And if there is a need you can also create an interface that will extend the base one:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity\Invoice;
|
||||
|
||||
use Sylius\InvoicingPlugin\Entity\InvoiceInterface as BaseInvoiceInterface;
|
||||
|
||||
interface InvoiceInterface extends BaseInvoiceInterface
|
||||
{
|
||||
public function customFunction(): mixed;
|
||||
}
|
||||
|
||||
Now let's add those classes to the configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/packages/_sylius.yaml
|
||||
sylius_invoicing:
|
||||
resources:
|
||||
invoice:
|
||||
classes:
|
||||
model: App\Entity\Invoice\Invoice
|
||||
interface: App\Entity\Invoice\InvoiceInterface
|
||||
|
||||
.. note::
|
||||
|
||||
Don't forget to update your database if you are changing/adding fields.
|
||||
|
||||
Now you can show a new invoice table on PDF with some changes just like in chapters before.
|
||||
170
docs/cookbook/invoices/generating-invoice-after-payment.rst
Normal file
170
docs/cookbook/invoices/generating-invoice-after-payment.rst
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
How to have the Invoice generated after the payment is paid?
|
||||
============================================================
|
||||
|
||||
.. note::
|
||||
|
||||
This cookbook describes customization of a feature available only with `Sylius/InvoicingPlugin <https://github.com/Sylius/InvoicingPlugin/>`_ installed.
|
||||
|
||||
The invoicing plugin lets you generate and download PDF invoices regarding orders. In its default behavior the invoice
|
||||
is generated right after the customer creates and places the order.
|
||||
In this cookbook, we will describe how to change this behavior, so the invoice will be created right after the order would be paid.
|
||||
|
||||
Why would you customize the invoice generation?
|
||||
-----------------------------------------------
|
||||
|
||||
In the default case the order items should not be changed after completing order. But let's say that your shop is customized with some logic
|
||||
which is not out of the box. Maybe one of these changes will let you change the order after it is placed?
|
||||
In this case, it would be better to have an invoice generated after a particular step (in the case of this cookbook - after the order is paid).
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
Before you start, make sure that you have:
|
||||
|
||||
#. `Sylius/InvoicingPlugin <https://github.com/Sylius/InvoicingPlugin/>`_ installed.
|
||||
#. `Wkhtmltopdf <https://wkhtmltopdf.org/>`_ package installed, because most of pdf generation is done with it.
|
||||
|
||||
How to customize the invoice generation?
|
||||
----------------------------------------
|
||||
|
||||
The concept is quite straightforward and it is based on Symfony events and event listeners.
|
||||
We need to create 3 classes and declare them in config files.
|
||||
Let's start first with EventListeners that will override the default ones:
|
||||
|
||||
**1.** The ``OrderPaymentPaidListener`` will create an invoice and check if it does not exist:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\EventListener;
|
||||
|
||||
use Sylius\InvoicingPlugin\Command\SendInvoiceEmail;
|
||||
use Sylius\InvoicingPlugin\Creator\InvoiceCreatorInterface;
|
||||
use Sylius\InvoicingPlugin\Event\OrderPaymentPaid;
|
||||
use Sylius\InvoicingPlugin\Exception\InvoiceAlreadyGenerated;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
final class OrderPaymentPaidListener
|
||||
{
|
||||
/** @var InvoiceCreatorInterface */
|
||||
private $invoiceCreator;
|
||||
|
||||
/** @var MessageBusInterface */
|
||||
private $commandBus;
|
||||
|
||||
public function __construct(InvoiceCreatorInterface $invoiceCreator, MessageBusInterface $commandBus)
|
||||
{
|
||||
$this->invoiceCreator = $invoiceCreator;
|
||||
$this->commandBus = $commandBus;
|
||||
}
|
||||
|
||||
public function __invoke(OrderPaymentPaid $event): void
|
||||
{
|
||||
try {
|
||||
$this->invoiceCreator->__invoke($event->orderNumber(), $event->date());
|
||||
} catch (InvoiceAlreadyGenerated $exception) {
|
||||
}
|
||||
|
||||
$this->commandBus->dispatch(new SendInvoiceEmail($event->orderNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
**2.** The ``NoInvoiceOnOrderPlacedListener`` will not do anything (what a lazy boy), but as we are changing the behavior, it still has very important role:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\EventListener;
|
||||
|
||||
use Sylius\InvoicingPlugin\Event\OrderPlaced;
|
||||
|
||||
final class NoInvoiceOnOrderPlacedListener
|
||||
{
|
||||
public function __invoke(OrderPlaced $event): void
|
||||
{
|
||||
// intentionally left blank
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
**3.** Last but not least ``OrderPaymentPaidProducer`` which will dispatch an event at a correct moment:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Producer;
|
||||
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Model\PaymentInterface;
|
||||
use Sylius\InvoicingPlugin\DateTimeProvider;
|
||||
use Sylius\InvoicingPlugin\Event\OrderPaymentPaid;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
final class OrderPaymentPaidProducer
|
||||
{
|
||||
/** @var MessageBusInterface */
|
||||
private $eventBus;
|
||||
|
||||
/** @var DateTimeProvider */
|
||||
private $dateTimeProvider;
|
||||
|
||||
public function __construct(
|
||||
MessageBusInterface $eventBus,
|
||||
DateTimeProvider $dateTimeProvider
|
||||
) {
|
||||
$this->eventBus = $eventBus;
|
||||
$this->dateTimeProvider = $dateTimeProvider;
|
||||
}
|
||||
|
||||
public function __invoke(PaymentInterface $payment): void
|
||||
{
|
||||
/** @var OrderInterface|null $order */
|
||||
$order = $payment->getOrder();
|
||||
if ($order === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var string $number */
|
||||
$number = $order->getNumber();
|
||||
$this->eventBus->dispatch(new OrderPaymentPaid($number, $this->dateTimeProvider->__invoke()));
|
||||
}
|
||||
}
|
||||
|
||||
**4.** Last thing that we need to do is to register new services in a container:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/services.yaml
|
||||
services:
|
||||
sylius_invoicing_plugin.listener.order_payment_paid:
|
||||
class: App\EventListener\OrderPaymentPaidListener
|
||||
arguments:
|
||||
- '@sylius_invoicing_plugin.creator.invoice'
|
||||
- '@sylius.command_bus'
|
||||
tags:
|
||||
- { name: messenger.message_handler }
|
||||
|
||||
sylius_invoicing_plugin.event_listener.order_placed:
|
||||
class: App\EventListener\NoInvoiceOnOrderPlacedListener
|
||||
tags:
|
||||
- { name: messenger.message_handler }
|
||||
|
||||
sylius_invoicing_plugin.event_producer.order_payment_paid:
|
||||
class: App\Producer\OrderPaymentPaidProducer
|
||||
arguments:
|
||||
- '@sylius.event_bus'
|
||||
- '@sylius_invoicing_plugin.date_time_provider'
|
||||
public: true
|
||||
|
||||
After these changes, the invoice will be generated after the order is paid, not just after it is placed.
|
||||
|
||||
.. image:: ../../_images/cookbook/generating-invoice-after-payment/before_payment.png
|
||||
2
docs/cookbook/invoices/map.rst.inc
Normal file
2
docs/cookbook/invoices/map.rst.inc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
* :doc:`/cookbook/invoices/custom-invoice`
|
||||
* :doc:`/cookbook/invoices/generating-invoice-after-payment`
|
||||
Loading…
Add table
Reference in a new issue