Git Product home page Git Product logo

invoicingplugin's Introduction

Sylius Logo.

Invoicing Plugin

This plugin creates an invoice related to the order.

SyliusInvoicingPlugin creates new immutable invoice when the order is in given state (default: created) and allows both customer and admin to download invoices related to the order.

Screenshot showing invoice Screenshot showing invoice browsing page in administration panel

Business value

The primary aim of Invoicing Plugin is to create a document representing Customer's will to buy particular products and pay for them.

An Invoice can also be treated as a proof of placing an Order. Thus, it is downloadable as .pdf file and can be sent to Customer manually by the Administrator or automatically once an Order is paid.

Additional feature of the plugin that fulfills Invoicing domain is the ability to set billing data on a Seller.

Installation

  1. Require plugin with composer:

    composer require sylius/invoicing-plugin

    Remember to allow community recipes with composer config extra.symfony.allow-contrib true or during plugin installation process

  2. Apply migrations to your database:

    bin/console doctrine:migrations:migrate
  3. Default configuration assumes enabled PDF file generation. If you don't want to use that feature change your app configuration:

    # config/packages/sylius_invoicing.yaml
    sylius_invoicing:
        pdf_generator:
            enabled: false

    Otherwise, check if you have wkhtmltopdf binary. If not, you can download it here.

    In case wkhtmltopdf is not located in /usr/local/bin/wkhtmltopdf modify the WKHTMLTOPDF_PATH environment variable in the .env file:

    WKHTMLTOPDF_PATH=/usr/local/bin/wkhtmltopdf # Change this! :)
    
  4. If you want to generate invoices for orders placed before plugin's installation run the following command using your terminal:

    bin/console sylius-invoicing:generate-invoices

Beware!

This installation instruction assumes that you're using Symfony Flex. If you don't, take a look at the legacy installation instruction. However, we strongly encourage you to use Symfony Flex, it's much quicker! :)

Extension points

Majority of actions contained in SyliusInvoicingPlugin is executed once an event after changing the state of the Order.

Here is the example for Winzou State Machine:

winzou_state_machine:
    sylius_payment:
        callbacks:
            after:
                sylius_invoicing_plugin_payment_complete_producer:
                    on: ['complete']
                    do: ['@sylius_invoicing_plugin.event_producer.order_payment_paid', '__invoke']
                    args: ['object']

Code placed above is a part of configuration placed in config.yml file. You can customize this file by adding new state machine events listeners or editing existing ones.

Here is the example for Symfony's workflow:

<service id="sylius_invoicing_plugin.event_listener.workflow.payment.produce_order_payment_paid"
       class="Sylius\InvoicingPlugin\EventListener\Workflow\Payment\ProduceOrderPaymentPaidListener">
   <argument type="service" id="sylius_invoicing_plugin.event_producer.order_payment_paid" />

   <tag name="kernel.event_listener" event="workflow.sylius_payment.completed.complete" priority="100" />
</service>

Apart from that an Invoice model is treated as a Resource.

You can read more about Resources here:

http://docs.sylius.com/en/latest/components_and_bundles/bundles/SyliusResourceBundle/index.html.

Hence, template for displaying the list of Invoices is defined in routing.yml file:

sylius_invoicing_plugin_invoice:
    resource: |
        alias: sylius_invoicing_plugin.invoice
        section: admin
        templates: "@SyliusAdmin\\Crud"
        only: ['index']
        grid: sylius_invoicing_plugin_invoice
        permission: true
        vars:
            all:
                subheader: sylius_invoicing_plugin.ui.manage_invoices
            index:
                icon: inbox
    type: sylius.resource

Another aspect that can be both replaced and customized is displaying Invoices list on Order show view. Code responsible for displaying Invoices related to the Order is injected to existing Sylius template using Sonata events. You can read about customizing templates via events here:

http://docs.sylius.com/en/latest/customization/template.html

Invoices files

By default, when the order is paid, an immutable Invoice pdf file is saved on the server. The save directory is specified with %sylius_invoicing.invoice_save_path% parameter, that can be overridden if needed.

There is no direct relation between Sylius\InvoicingPlugin\Entity\Invoice entity and its file. It's resolved based on the Invoice::$number, which is defined in Sylius\InvoicingPlugin\Provider\InvoiceFileProviderInterface service. By overriding this service, you can change a logic that is used to retrieve the invoice file.

Fixtures

You can add ShopBillingData fixtures into a yaml to add Channel ShopBillingData info to your installation. More instructions on the fixtures configuration instructions.

Security issues

If you think that you have found a security issue, please do not use the issue tracker and do not post it publicly. Instead, all security issues must be sent to [email protected].

invoicingplugin's People

Contributors

adamkasp avatar arti0090 avatar bartoszpietrzak1994 avatar bartoszwojdalowicz avatar bastiengoze avatar bigbadbassman avatar bitbager avatar coldic3 avatar damonsson avatar gsadee avatar jakubtobiasz avatar jbcr avatar kulczy avatar lchrusciel avatar mamazu avatar noresponsemate avatar pamil avatar pierre-grebert avatar pierre-h avatar prometee avatar rafikooo avatar roshyo avatar sirdomin avatar snowbaha avatar stloyd avatar tautelis avatar tom10271 avatar tomanhez avatar wojdylak avatar zales0123 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

invoicingplugin's Issues

Send Invoice on Order Placed

I miss the option to send out invoice depending on the payment method. Some need the invoice in advance.

I can not make it running. I guess im just to early, trying to send email before created i guess. Is there any help for me?

app.invoice_send_listener:
    class: App\EventListener\SendInvoiceListener
    arguments: ['sylius_invoicing_plugin.event_bus', 'Sylius\InvoicingPlugin\DateTimeProvider']
 
<?php

namespace App\EventListener;

use Sylius\InvoicingPlugin\Command\SendInvoiceEmail;
use Sylius\InvoicingPlugin\Event\OrderPlaced;
use Symfony\Component\Messenger\MessageBusInterface;

class SendInvoiceListener
{
    /** @var MessageBusInterface */
    private $commandBus;

    public function __construct(MessageBusInterface $commandBus)
    {
        $this->commandBus = $commandBus;
    }

    public function __invoke(OrderPlaced $event): void
    {
        $this->commandBus->dispatch(new SendInvoiceEmail($event->orderNumber()));
    }
}  


Change OrderPaymentPaid event to OrderPaid

There is case, when Order may have multiple valid payments with partial amounts, which sums up to Order total. (At least vanilla Sylius supports such behaviour with Order:paymentState->partially_paid and I personally have multi-payment case in one of my projects), so only Order:paymentState should be used to determine, if order is paid, not Payment:state.

InvoiceRepositoryInterface standardization

It seems that this plugin poorly follows Sylius standards:

  1. interface InvoiceRepository should be called interface InvoiceRepositoryInterface
  2. custom invoice repository class should be defined in Configuration.php so it is accessible in Sylius-like style by calling sylius_invoicing_plugin.repository.invoice
  3. Sylius\InvoicingPlugin\Repository\DoctrineInvoiceRepository should be moved to Sylius\InvoicingPlugin\Doctrine\ORM\InvoiceRepository
  4. Furthermore, there is no need for DoctrineInvoiceRepository since default Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository has all the implementation required. Just replace getOneByOrderNumber with findOneByOrderNumber.
  5. And finally, getters should start with get so that $invoice->orderNumber() becomes $invoice->getOrderNumber()

Or maybe it is made like this on purpose?

Make Invoice entity more customizable

Since we create a new Invoice by injecting all parameters using the constructor, it's impossible not to use a particular field unless overriding whole class.

Fresh installation under 1.4.0 not working?

Hi there,
after fresh Sylius 1.4.0 installation (Symfony 4.2.3) I can not edit the channel anymore:

Neither the property "billingData" nor one of the methods "getBillingData()", "billingData()", "isBillingData()", "hasBillingData()", "__get()" exist and have public access in class "App\Entity\Channel\Channel".

In my channel entity I miss the:
implements ShopBillingDataAwareInterface { use ShopBillingDataTrait; }

Also not possible to edit order status to complete.
InvalidArgumentException
HTTP 500 Internal Server Error
Sylius\InvoicingPlugin\Entity\InvoiceInterface

Or something missing in your installation docs?
Mike

Resending invoice use wrong locale

Hi,

In administration panel, when resending the invoice, the locale used is the one from the current logged user (administrator), and not the invoice locale.

I didn't investigate the code. I might do a PR (but don't know when)

Sylius Address

why the sylius_invoicing_plugin_billing_data table doesn't use sylius_address id ?

Make invoices and credit memos grid consistent

Invoices and credit memos (from RefundPlugin) are twin concepts and therefore, I believe, we should make them consistent as much as it's possible.

Looking at credit memos and invoices grids on order show page:

Zrzut ekranu 2019-03-13 o 12 50 48

And on overall indexes:

Zrzut ekranu 2019-03-13 o 12 55 41

Zrzut ekranu 2019-03-13 o 12 55 46

we can see a lot of things that can be unified ๐Ÿค–

Possible solution

My proposition for both invoices and credit memos overall grids is something like this:

Zrzut ekranu 2019-03-13 o 14 04 59

  1. Invoice/credit memo number
  2. Order number with # at the beginning and a link to order show page
  3. Channel name with a colorful circle
  4. Total
  5. Issued at date, in Y-m-d format
  6. Buttons
    • "Show" - green, eye
    • "Download" - purple, download icon
    • "Resend" - yellow, paper plane (not needed for credit memo for now)

On partial grids (displayed on order show page) we can get rid of 2. and 3. column, as they're irrelevant.

Tell me what you think about this proposition ๐Ÿ–– I feel strongly that these two plugins can be stabilized in the very nearest future ๐Ÿš€ ๐Ÿ”ฅ

Shouldn't MessageHandlers be configured to only process messages from this plugin's message buses ?

I am developing a plugin for sylius, and, as with this plugin, I am using messenger.

When debugging my code with php bin/console debug:messenger I can see that this plugins message handlers are listening to all buses.

While this is not an urgent issue, and may not even produce any errors in most cases, surely, for maximum interoperability with other plugins/bundles, unless there is a specific reason why these handlers need to listen on other buses, they should probably be restricted to the buses defined in this bundle.

Mainly I'm asking in order to validate whether i need to do this in my own bundle...

ChannelTypeExtension not found

Version 0.8.0 : ChannelTypeExtension is not present and this error appears : Attempted to load class "ChannelTypeExtension" from namespace "Sylius\InvoicingPlugin\Form\Extension".
Did you forget a "use" statement for "Sylius\Bundle\CoreBundle\Form\Extension\ChannelTypeExtension"?

Download/pdf.html.twig needs html tag

Just a little thing:
InvoicingPlugin/src/Resources/views/Invoice/Download/pdf.html.twig

To work with footer and header there must be the <!DOCTYPE html> tag.
With footer and header - there will be errors in rendering the *pdf. Like no header, but footer or no footer, but header or all white.... especially if there is lot of content (more than one side).

{% set shopBillingData = invoice.shopBillingData %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <style>

[Feature] Add ability to resend automatically failed generated PDF

I know this is possible through the admin order area, but this is the use case :

If we want to extends this plugin and give us the ability to build invoice PDF through something else than wkhtmltopdf like an external API (Quickbooks, SageOne, etc) we have to be able to stop the invoice mail sending if something get wrong generating an Exception. But this Exception will stop the order process and we have to take care about our customers experience first.

What I suggest is to give us the ability to return null when using Sylius\InvoicingPlugin\Generator\InvoicePdfFileGeneratorInterface::generate(string $invoiceId): InvoicePdf

So if the PDF is not generated (is null) we don't send the mail.

But we have to store somewhere the fact we don't send it ($invoice->setPdfSent(true);) and also retry to send it later with a Command like sylius-invoicing:generate-invoices --only-not-sent=1.

What do you think of this feature ?
Do you see any enhancement to add to this feature ?

InvalidArgumentException thrown in DoctrineInvoiceRepository

Attempting to complete orders that existed before the InvoicingPlugin was added, causes an InvalidArgumentException to be thrown because the order number doesn't exist sylius_invoicing_plugin_invoice table.

$invoice = $this->entityRepository->findOneBy(['orderNumber' => $orderNumber]);
Assert::notNull($invoice, InvoiceInterface::class);

For orders placed after the plugin is added, it works as expected.

Route "sylius_invoicing_plugin_admin_invoice_index" does not exist.

When installing and configuring the plugins when I access the admin menu I have the following error:
An exception has been thrown during the rendering of a template ("Unable to generate a URL for the named route" sylius_invoicing_plugin_admin_invoice_index "as such route does not exist.").

In the class Sylius\InvoicingPlugin\Ui\Menu\AdminMenuListener this route is called but in the path files:
Resources\config\app\routing
admin_invoicing.yml and shop_invoicing.yml
does not exist.

BillingData and ShopBillingData difference?

What is the difference between BillingData and ShopBillingData?
Why one is declared in Sylius\InvoicingPlugin\DependencyInjection\Configuration and the other is not?
Relations should point interfaces not models.

customize template

Hi, i tried to customise pdf template like other bundle but nothing change : /

my path is Templates/SyliusInvoicingPlugin/Invoice/Download/ ..

edit : the path is actually : Templates/bundles/SyliusInvoicingPlugin/Invoice/Download/ ..

do you know why ?

thx you !

Generating an invoice only after payment is done

Hello,

My problem is that the invoices are generated when an order is placed, I would like to have it generated when the order is paid. I've been trying to mess around in the config but I couldn't figure it out.

Thanks in advance !

Partial Invoicing

Currently, you can only create an invoice for the entire purchase order.

There are business cases for which a partial invoice is necessary.

interface InvoiceGeneratorInterface
{
    public function generateForOrder(OrderInterface $order, \DateTimeInterface $date): InvoiceInterface;

    /**
     * @param array $orderItemData [['orderItem' => $orderItem1, 'quantity' => 1],['orderItem' => $orderItem2, 'quantity' => 1]]
     * @param int $shippingAmount Shipping amount for this invoivce
     * @param \DateTimeInterface $date
     * @return InvoiceInterface
     */
    public function generateForOrderItems(array $orderItemData, int $shippingAmount, \DateTimeInterface $date): InvoiceInterface;
}

ShopBillingData should be generated for each invoice.

Hello,

There are some tickets talking about ShopBillingData #52 and #83 but my request is not exactly the same. If an invoice is generated twice, for eg with this use case :

  1. Order is placed, invoice generated, user download it.
  2. Shop admin, change shop billing data in channel (company is sold, commercial name change)
  3. user come back to it's order and download his invoice again.

Between step 1 and 3 ShopBillingData has changed so invoice has changed, and that's wrong, because invoice wasn't done by current (new) company but by previous one.

Twig Error Runtime

After installing the InvoicingPlugin and visiting the subsequent invoice link localhost:8000/invoices as an admin, I get the error Twig_Error_runtime (Impossible to access an attribute ("email") on a null variable.). I thought for starters the link in the admin section should have been /admin/invoices.

GenerateInvoicesCommand.php will select orders without order number

#148

Root cause:
vendor/sylius/invoicing-plugin/src/Cli/GenerateInvoicesCommand.php

$orders = $this->orderRepository->findAll();

Fix by:

/** @var array $orders */
$orders = $this->orderRepository
    ->createQueryBuilder('o')
    ->where('o.number IS NOT NULL')
    ->getQuery()
    ->getResult();

Error message:

symfony console sylius-invoicing:generate-invoices -vvv

In InvoiceCreator.php line 35:
                                                                                                                                                                                                                                                         
  [Symfony\Component\Debug\Exception\FatalThrowableError]                                                                                                                                                                                                
  Argument 1 passed to Sylius\InvoicingPlugin\Creator\InvoiceCreator::__invoke() must be of the type string, null given, called in ~/Sites/hbx-cn/vendor/sylius/invoicing-plugin/src/Creator/MassInvoicesCreator.php on line 32  
                                                                                                                                                                                                                                                         

Exception trace:
 () at ~/Sites/hbx-cn/vendor/sylius/invoicing-plugin/src/Creator/InvoiceCreator.php:35
 Sylius\InvoicingPlugin\Creator\InvoiceCreator->__invoke() at ~/Sites/hbx-cn/vendor/sylius/invoicing-plugin/src/Creator/MassInvoicesCreator.php:32
 Sylius\InvoicingPlugin\Creator\MassInvoicesCreator->__invoke() at ~/Sites/hbx-cn/vendor/sylius/invoicing-plugin/src/Cli/GenerateInvoicesCommand.php:36
 Sylius\InvoicingPlugin\Cli\GenerateInvoicesCommand->execute() at ~/Sites/hbx-cn/vendor/symfony/console/Command/Command.php:255
 Symfony\Component\Console\Command\Command->run() at ~/Sites/hbx-cn/vendor/symfony/console/Application.php:939
 Symfony\Component\Console\Application->doRunCommand() at ~/Sites/hbx-cn/vendor/symfony/framework-bundle/Console/Application.php:87
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at ~/Sites/hbx-cn/vendor/symfony/console/Application.php:273
 Symfony\Component\Console\Application->doRun() at ~/Sites/hbx-cn/vendor/symfony/framework-bundle/Console/Application.php:73
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at ~/Sites/hbx-cn/vendor/symfony/console/Application.php:149
 Symfony\Component\Console\Application->run() at ~/Sites/hbx-cn/bin/console:38

sylius-invoicing:generate-invoices [-h|--help] [-q|--quiet] [-v|vv|vvv|--verbose] [-V|--version] [--ansi] [--no-ansi] [-n|--no-interaction] [-e|--env ENV] [--no-debug] [--] <command>

exit status 255

Mark items in the list with VAT

If I have different VAT, like normal and reduced, I need to make clear in my invoice the products VAT.
Best way in my opinion, just put a * for first VAT, ** for second a.o. ...

Extendable Entities

All Doctrine entities should be mapped-superclass instead of entity to be extendable.

For exemple my use case is to just alter the PDF generation by getting the PDF from Quickbooks or Sageone. So I just need to add a field on the Invoice entity to record the external_invoice_id and decorate InvoicePdfFileGenerator (download the PDF content) and InvoiceGenerator (create the external invoice with the APIs of Sageone or Quickbooks).

But right now I have to create a new Entity referencing a wall new table instead of just add a new field into the Invoice entity :$

Extending Invoice

Hi,
I am using dev-master of plugin and dev-master of Sylius.

I added this to config

sylius_invoicing:
    resources:
        invoice:
            classes:
                model: App\Entity\Invoice\Invoice

and this is my new entity, there is nothing inside

<?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
{
}

i am getting this errror

Return value of Sylius\InvoicingPlugin\Entity\Invoice::channel() must be an instance of Sylius\InvoicingPlugin\Entity\InvoiceChannelInterface, null returned

I understand that the problem is with embedded properties, but I am not figuring out how to solve this.

ShopBillingData on invoice show should be taken from channel

Right now shop billing data is taken from

{% set billingData = sylius.channel.billingData %}

and should be

{% set billingData = channel.billingData %}

where channel should be taken from ChannelRepository using channelCode from InvoiceChannel

Add fixtures abilities

It could be great if we could add channel extra informations in a fixture.

Exemple :

sylius_fixtures:
    suites:
        my_own_fixture:

            listeners:
                orm_purger: ~
                logger: ~

            fixtures:

                channel:
                    options:
                        custom:
                            default_web_store:
                                enabled: true
                                name: "Web Store"
                                code: "default"
                                contact_email: "[email protected]"
                                default_locale: "%locale%"
                                hostname: null
                                locales:
                                    - "en_US"
                                    - "fr_FR"
                                    - "es_ES"
                                    - "de_DE"
                                base_currency: "USD"
                                currencies:
                                    - "USD"
                                    - "EUR"
                                billing_data:
                                    company: 'My company'
                                    country_code: 'US'
                                    city: 'My city'
                                    postcode: '00000'
                                    tax_id: '1234567890'
                                    street_address: '1 my street address'

Correct path for kpn_snappy header/footer

SyliusInvoicePlugin / kpn Snappy can not get the header from twig. I createt a testheader in same folder like pdf.html.twig. /src/Resources/SyliusInvoicingPlugin/views/Invoice/Download

But its not working to render them in my pdf.

knp_snappy:
    pdf:
        enabled:    true
        binary:     '%env(WKHTMLTOPDF_PATH)%'
        options:
            - { name: 'header-html', value: '@SyliusInvoicingPlugin/Invoice/Download/_header.html.twig' }

But if i write html direkt in the knp_snappy.yaml it works.

- { name: 'header-html', value: '<!DOCTYPE html>
                                             <html>
                                                 <body>
                                                     <div style="border: 5px dashed crimson; color: maroon; background-color: darksalmon">
                                                         This is a header
                                                     </div>
                                                 </body>
                                             </html>' }

Any idea?

I found a solution, but not so nice.

no twig and I must put the path like this:

- { name: 'header-html', value: `'%kernel.project_dir%/src/Resources/SyliusInvoicingPlugin/views/Invoice/Download/_header.html' }

framework.messenger config error since symfony 4.3.1

Hello,
Upgraded Sylius to 1.5.1 SF 1.5.1 via composer.
Had this error:
Invalid configuration for path "framework.messenger": You must specify the "default_bus" if you define more than one bus.

Setting a default_bus in /vendor/sylius/invoicing-plugin/src/Resources/config/config.yml
fixed the issue.

framework:
     messenger:
        default_bus: sylius_invoicing_plugin.command_bus
        buses:
            sylius_invoicing_plugin.command_bus: ~
            sylius_invoicing_plugin.event_bus:
                default_middleware: allow_no_handlers

Or better in {App}/config/packages/messenger.yaml until fixed in this plugin.

framework:
    messenger:
        default_bus: sylius_invoicing_plugin.command_bus

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.