flux-se/sylius-eu-vat-plugin

Add EU VAT field and validation for Sylius using VIES API Webservice.

Installs: 5 862

Dependents: 0

Suggesters: 0

Security: 0

Stars: 13

Watchers: 4

Forks: 7

Open Issues: 2

Type:sylius-plugin


README

Latest Version on Packagist Software License Build Status Quality Score

EU VAT field and validation for Sylius using VIES API Webservice

This plugin is adding :

  • two new attributes to the Channel entity allowing to know from which country your shop is from and what's the European zone to target
  • VAT Number field to the Address entity
  • validation over VIES API on this field
  • validation of the address country (vat number country should equal to the address country)
  • the basic European rule allowing the seller not to charge VAT to foreign European customers who have provided a valid VAT number :
    • (Shop country === customer country vat number) then do nothing
    • (Shop country !== customer country vat number) then remove taxes

Screenshots

Shop checkout

Shop checkout address step form

Shop checkout summary page

Shop account

Shop account address book

Shop account order details

Installation

composer require flux-se/sylius-eu-vat-plugin

Configuration

Enable this plugin :

<?php

# config/bundles.php

return [
    // ...
    FluxSE\SyliusEUVatPlugin\FluxSESyliusEUVatPlugin::class => ['all' => true],
    // ...
];

Add default config if you want to get access to the default fixtures this plugin need.

# config/packages/flux_se_sylius_eu_vat.yaml

imports:
    - { resource: "@FluxSESyliusEUVatPlugin/Resources/config/config.yaml" }

Copy Sylius overridden templates to your templates directory (e.g templates/bundles/):

mkdir -p templates/bundles/SyliusAdminBundle/
cp -R vendor/flux-se/sylius-eu-vat-plugin/src/Resources/views/SyliusAdminBundle/* templates/bundles/SyliusAdminBundle/
mkdir -p templates/bundles/SyliusShopBundle/
cp -R vendor/flux-se/sylius-eu-vat-plugin/src/Resources/views/SyliusShopBundle/* templates/bundles/SyliusShopBundle/

Update Channel entity : src/Entity/Channel/Channel.php

<?php

declare(strict_types=1);

namespace App\Entity\Channel;

use Doctrine\ORM\Mapping as ORM;
use FluxSE\SyliusEUVatPlugin\Entity\EuropeanChannelAwareTrait;
use Sylius\Component\Core\Model\Channel as BaseChannel;

/**
 * @ORM\Entity()
 * @ORM\Table(name="sylius_channel")
 */
class Channel extends BaseChannel implements ChannelInterface
{
    use EuropeanChannelAwareTrait;
}

And the corresponding interface : src/Entity/Channel/ChannelInterface.php

<?php

declare(strict_types=1);

namespace App\Entity\Channel;

use FluxSE\SyliusEUVatPlugin\Entity\EuropeanChannelAwareInterface;
use Sylius\Component\Core\Model\ChannelInterface as BaseChannelInterface;

interface ChannelInterface extends BaseChannelInterface, EuropeanChannelAwareInterface
{
}

If you are using YAML orm definitions :

# config/doctrine/Address.orm.yml

App\Entity\Adressing\Address :
    type: entity
    table: sylius_address

    fields:
        vatNumber:
            name: vat_number
            type: string
            nullable: true

Then change the default Sylius model class :

# config/packages/sylius_channel.yaml

sylius_channel:
    resources:
        channel:
            classes:
                model: App\Entity\Channel\Channel

Update Address entity : src/Entity/Addressing/Address.php

<?php

declare(strict_types=1);

namespace App\Entity\Addressing;

use Doctrine\ORM\Mapping as ORM;
use FluxSE\SyliusEUVatPlugin\Entity\VATNumberAwareTrait;
use Sylius\Component\Core\Model\Address as BaseAddress;

/**
 * @ORM\Entity()
 * @ORM\Table(name="sylius_address")
 */
class Address extends BaseAddress implements AddressInterface
{
    use VATNumberAwareTrait;
}

And the corresponding interface : src/Entity/Addressing/AddressInterface.php

<?php

declare(strict_types=1);

namespace App\Entity\Addressing;

use FluxSE\SyliusEUVatPlugin\Entity\VATNumberAwareInterface;
use Sylius\Component\Core\Model\AddressInterface as BaseAddressInterface;

interface AddressInterface extends BaseAddressInterface, VATNumberAwareInterface
{
}

If you are using YAML orm definitions :

# config/doctrine/Channel.orm.yml

App\Entity\Channel\Channel:
    type: entity
    table: sylius_channel

    manyToOne:
        baseCountry:
            targetEntity: Sylius\Component\Addressing\Model\CountryInterface
            fetch: EAGER
            joinColumn:
                name: base_country_id
                onDelete: "SET NULL"
        europeanZone:
            targetEntity: Sylius\Component\Addressing\Model\ZoneInterface
            fetch: EAGER
            joinColumn:
                name: european_zone_id
                onDelete: "SET NULL"

Then change the default Sylius model class :

# config/packages/sylius_addressing.yaml

sylius_addressing:
    resources:
        address:
            classes:
                model: App\Entity\Addressing\Address

Update your database :

# This bundle use doctrine migrations ^3 (see `src/Migrations` folder for more details)
php ./bin/console doctrine:migrations:migrate

Load some required fixtures :

./bin/console sylius:fixture:load european_vat_zones

Go to your admin panel and edit your Channel to set the two fields to indicate to this plugin :

  1. What is your base country to compare to your customer country.
  2. What is the European zone to know if the customer is part of the Europe or not.

Fixtures

You can add some fixtures to auto-configure your channel, for example add this into a yaml file :

# config/packages/my_fixtures.yaml

sylius_fixtures:
    suites:
    
        french_european_channel:    
        
            listeners:
                logger: ~
                
            fixtures:
                
                address_with_vat_number:
                    options:
                        custom:
                            my_customer:
                                first_name: "John"
                                last_name: "Doe"
                                phone_number: "+33912345678"
                                company: "My Company Inc."
                                street: "1234 Street Avenue"
                                city: "London"
                                postcode: "1234"
                                country_code: "GB"
                                customer: "john.doe@mycompany.com"
                                vat_number: ~ # could also be "GB123456789"
            
                eu_vat_plugin_european_channel:
                    options:
                        custom:
                            default:
                                channel: "default_channel_code" # Put an existing Channel code
                                base_country: "FR" # Existing Country code
                                european_zone: "EU" # Existing Zone code