*Title: Improved handling of VAT charging.

*Incentive:

Currently, VAT charging functionality in eZ Publish is quite primitive.
We can only assign fixed VAT to a product content class. However, VAT
charging rules may be much more complex, and we should provide support
for such rules.

*Basics:

What we need is ability to charge different VAT percentage depending on
a specific product and country a buyer is from. We should not enforce
any specific charging logic. Instead, it should be up to developer to
implement the logic a webshop needs. Thus, we provide an interface for
so-called "VAT handlers". We ship a default VAT handler implementing
some basic logic that can be overriden with custom VAT handlers, if
needed.

*Implementation details:

1. Handler interface

A VAT handler is a file that contains a class implementing the following
method:

 /**
  *
  * \public
  * \static
  * \param $object   The product content object.
  * \param $country  Country the buyer is from, or false if not specified.
  * \return          VAT percent (integer), or null in case of an error.
  */
 mixed function getVatPercent( eZContentObject $object, mixed $country );

A handler is not called directly, but via eZVATManager class.
Method getVAT() of that class returns VAT percentage that should be
charged for a given product:

 $vatPercent = eZVATManager::getVAT( $object, $country );

All that getVAT() method does is invoking getVatPercent() method of the
handler specified in ini settings.

2. Default VAT handler

The default VAT handler shipped with eZ Publish uses special rules to
determine the appropriate VAT for a product. Each rule determines what VAT
to charge for a given product category and country the customer is from.
Those rules (stored in an external DB table) can be managed via the
admin. interface.

Example:
.---------+---------------------------+-----------------------.
| Country | Category                  |      VAT              |
+=========+===========================+=======================+
| Norway  | food, drinks              | Norwegian / Reduced   |
+---------+---------------------------+-----------------------+
| Norway  | *                         | Norwegian / Standard  |
+---------+---------------------------+-----------------------+
| Germany | food, books, newspapers   | German / Reduced      |
+---------+---------------------------+-----------------------+
| Germany | *                         | German / Standard     |
+---------+---------------------------+-----------------------+
| Ukraine | *                         | Ukrainian / Standard  |
+---------+---------------------------+-----------------------+
| *       | *                         | None                  |
`---------+---------------------------+-----------------------'

The rightmost table column refers to VAT types defined in Shop tab of
the admin. interface. Asterisk (*) means "any category (country)",
depending on which column it is specified in.

The rules in the table above assign reduced VAT to some
products in Germany and Norway; for all other products in
Norway, Ukraine and Germany the standard VAT is used. For the rest of
countries no VAT is charged.

_Category_ of a product is a special content attribute of a product
class, used by the default VAT handler when determining VAT for the
object. The datatype of this attribute uses database table to store
categories list. There is GUI for categories management in the admin.
interface.

User _country_  is a content attribute of type ezcountry added to a user
class. The list of countries is loaded from an .ini file.

The more exact match a rule provides for given country/category pair,
the higher priority it has. That means that the handler tries to always
choose the best matching VAT.

Exact match is when category or country is specified literally, not as '*'.

VAT choosing algorithm: possible match cases and their priorities:
4. exact match on country, exact match on category (e.g.: Norway =>  Food, Drinks)
3. exact match on country, weak match on category: (e.g.: Norway => *)
2. weak match on counry, exact match on category:  (e.g.: * => Food, Drinks)
1. weak match on country, weak match on category:  (e.g.: * => *)
0. no match on country and/or no match on category.


*Setup

1. Updating database schema

First, if you are upgrading to 3.8, you should run the apropriate DB
update script, e.g. dbupdate-3.6.0-to-3.8.0.sql.

2. Specifying a handler to use

VAT Handler to use is defined by the following settings in shop.ini:

###############################################################################
[VATSettings]

# Specifies VAT handler class/file name. For example, if the value is  
# "ezdefault", then the handler class should be named
# eZDefaultVATHandler (case does not matter) and placed to file
# ezdefaultvathandler.php residing in a directory specified by
# "RepositoryDirectories" and "ExtensionDirectories" settings.
Handler=ezdefault

# Directories where VAT handlers should be searched.
RepositoryDirectories[]=kernel/classes/vathandlers

# If you are going to implement your VAT handler in an extension
# then you should add the extension name to the list below.
# In that case VAT handlers will be searched for in the following directory:
# extension/<your_extension>/vathandlers
ExtensionDirectories[]
###############################################################################

3. Setting up the default VAT handler

If you want to use the default VAT handler shipped with eZ Publish
then you perform the steps described in this section.

 3.1. Creating product categories.

Fist, go to "Webshop" -> "Product categories" and create as many product
categories as you need. Then will be used to determine appropriate VAT
type for products being bought.

 3.2. Assigning categories to products and countries to users.

All your products should be assigned a category. Just add an
attribute of ezproductcategory datatype to your product class and then
go through your products assigning them categories. Then specify the
attribute identifier in shop.ini.[VATSettings].ProductCategoryAttribute
ini setting.

All users should be assigned a country. You don't have to do that
manually: if a user that doesn't have a country assigned tries to buy
something he will be asked to specify his/her country. All you need to
do is to add an attribute of ezcountry datatype to your user class. Then
specify the attribute identifier in
shop.ini.[VATSettings].UserCountryAttribute ini setting.

It is also recommended that you add a possibility for a user to choose
his/her country "on-the-fly" using the "user_country" toolbar. To do this,
add the following line into the "[Toolbar_right]" section of the
"settings/siteaccess/example/toolbar.ini.append.php" file where
"example" is your siteaccess name:

Tool[]=user_country

This setting instructs the system to display country selection tool on
the right toolbar. If a user selects a country there he/she will
immediately see updated product prices according to VAT charged for the
selected country.

For this to work properly with content caching, you should also add the
following line into the "[ContentSettings]" section of the
"site.ini.append.php" file located in the "settings/override/"
directory:

CachedViewPreferences[full]=user_preferred_country

 3.3. Creating VAT charging rules.

VAT rules is something that builds up VAT charging logic for your shop.
Go to "Webshop" -> "VAT rules" and create the VAT charging rules you
need. Every rule consists of a country, a set of product category and a
VAT type. A rule defines VAT type to use in case when user is from
specified country and the product belongs to one of specified
categories.


4. Setting up custom VAT handler

If the default VAT handler doesn't fit your needs you can implement your
own one. This section describes how to do that.

For example, we implement a simple VAT handler that determines VAT
percentage depending on the section a product belongs to. We place the
handler to an extension since it's not recommended to modify eZ Publish
kernel.

 4.1. Creating extension

Start by creating a new extension called "myshop" (create a new folder
called myshop in the extension/ directory).

 4.2 Creating the necessary directory structure.

Create a subdirectory called settings in the myshop/ directory.
This directory will contain all the settings that belong to the extension.

Create a subdirectory called vathandlers in the myshop/ directory.
This directory will contain your VAT handler.

 4.3 Creating your own VAT handler

Go to extension/myshop/vathandlers and create file called
mysectionbasedvathandler.php. Paste the following text into it:

#############################################################################
<?php
class MySectionBasedVATHandler
{
    /**
     * \public
     * \static
     */
    function getVatPercent( $object, $country )
    {
        $section = $object->attribute( 'section_id' );
        
        if ( $section == 1 )
            $percentage = 10;
        else
            $percentage = 20;

        return $percentage;
    }
}
?>
#############################################################################

 4.4 Editing ini settings

Go to extension/myshop/settings and create file called
shop.ini.append. Paste the following text into it:

###################################
[VATSettings]
ExtensionDirectories[]=myshop
Handler=mysectionbased
RequireUserCountry=false
DynamicVatTypeName=Section-based
###################################

This will tell eZ Publish to search for file
mysectionbasedvathandler.php in extension/myshop/vathandlers directory.
We set RequireUserCountry to false to disable error messages about
missing user country.

 4.5 Activating the extension.

Go to "Setup" -> "Extensions" in the admin interface and activate your
extension there by marking "myshop" extension and pressing "Apply changes".


*New views:

/shop/vatrules          -- The list of VAT charging rules.
/shop/editvatrule       -- Add/edit a VAT charging rule.
/shop/productcategories -- The list of product categories.


*New settings:

shop.ini:
 
 [VATSettings]
  Handler=
  RepositoryDirectories[]
  ExtensionDirectories[]
  ProductCategoryAttribute=
  UserCountryAttribute=
  DynamicVatTypeName=

See settings/shop.ini for more information.

*New fetch functions:

Fetch the list of all product categories:
 fetch( shop, product_category_list )

Fetch given product category:
 fetch( product_category, hash( category_id, $catID ) )

*Database schema changes:

Three new tables have been added to store VAT rules and product categories:

CREATE TABLE ezproductcategory (
  id int(11) NOT NULL auto_increment,
  name varchar(255) NOT NULL default '',
  PRIMARY KEY (id)
);

CREATE TABLE ezvatrule (
  id int(11) NOT NULL auto_increment,
  country varchar(255) NOT NULL default '',
  vat_type int(11) NOT NULL,
  PRIMARY KEY (id)
);

CREATE TABLE ezvatrule_product_category (
  vatrule_id int(11) NOT NULL,
  product_category_id int(11) NOT NULL,
  PRIMARY KEY (vatrule_id, product_category_id)

*Notes:

It is possible to integrate alternative country datatypes in the VAT
charging functionality. The integration actually means that country
should be stored in the same way by the datatype, by the VAT rules
management interface and by the shop user registration module
(shop/userregister).

Here are the steps to achieve this:

1. Make sure your datatype's content is either a hash having "value" key 
or an object capable of getting and setting "value" attribute (a-la 
eZPersistentObject). It doesn't matter how the content is actually 
stored to database, but objectAttributeContent() method must return an 
array/object. The value (usually a country code) is then compared to VAT
rules' countries.

2. Override design/standard/templates/shop/country/{view,edit}.tpl 
templates in your datatype extension so that countries can be displayed 
and edited in the VAT rules management interface and shop/userregister.

*References:

http://ez.no/community/developer/specs/improved_handling_of_vat_charging_and_shipping
