TaxManager is now a service in the container.

This commit is contained in:
Franck Allimant
2014-01-22 01:18:28 +01:00
parent c9c489a76b
commit 5d92ea0bab
23 changed files with 414 additions and 152 deletions

View File

@@ -20,7 +20,7 @@
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\TaxEngine\TaxType;
namespace Thelia\TaxEngine;
use Thelia\Exception\TaxEngineException;
use Thelia\Model\Product;
@@ -33,14 +33,54 @@ use Thelia\Type\TypeInterface;
*/
abstract class BaseTaxType
{
protected $requirements = null;
/**
* A var <-> value array which contains TaxtType requirements (e.g. parameters)
*
* @var array
*/
protected $requirements = array();
abstract public function pricePercentRetriever();
/**
* For a price percent tax type, return the percentage (e.g. 20 for 20%) of the product price
* to use in tax calculation.
*
* For other tax types, this method shoud return 0.
*
* @return number
*/
public function pricePercentRetriever() {
return 0;
}
abstract public function fixAmountRetriever(Product $product);
/**
* For constant amount tax type, return the absolute amount to use in tax calculation.
*
* For other tax types, this method shoud return 0.
*
* @return number
*/
public function fixAmountRetriever(Product $product) {
return 0;
}
abstract public function getRequirementsList();
/**
* Returns the requirements definition of this tax type. This is an array of
* TaxTypeRequirementDefinition, which defines the name and the type of
* the requirements. Example :
*
* array(
* 'percent' => new FloatType()
* );
*
* @return array of TaxTypeRequirementDefinition
*/
public function getRequirementsDefinition() {
return array();
}
/**
* @return the name of this tax type.
*/
abstract public function getTitle();
public function calculate(Product $product, $untaxedPrice)
@@ -48,41 +88,50 @@ abstract class BaseTaxType
return $untaxedPrice * $this->pricePercentRetriever() + $this->fixAmountRetriever($product);
}
/**
* @throws TaxEngineException
* @return array Return the requirements array.
*/
public function getRequirements() {
return $this->requirements;
}
public function loadRequirements($requirementsValues)
{
$this->requirements = $this->getRequirementsList();
$requirements = $this->getRequirementsDefinition();
if (!is_array($this->requirements)) {
throw new TaxEngineException('getRequirementsList must return an array', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD);
if (!is_array($requirements)) {
throw new TaxEngineException('getRequirementsDefinition must return an array', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD);
}
foreach ($this->requirements as $requirement => $requirementType) {
if (!$requirementType instanceof TypeInterface) {
throw new TaxEngineException('getRequirementsList must return an array of TypeInterface', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD);
foreach ($requirements as $requirement) {
$requirementName = $requirement->getName();
if (! array_key_exists($requirementName, $requirementsValues)) {
throw new TaxEngineException('Cannot load requirements : requirement value for `' . $requirementName . '` not found', TaxEngineException::TAX_TYPE_REQUIREMENT_NOT_FOUND);
}
if (!array_key_exists($requirement, $requirementsValues)) {
throw new TaxEngineException('Cannot load requirements : requirement value for `' . $requirement . '` not found', TaxEngineException::TAX_TYPE_REQUIREMENT_NOT_FOUND);
if (! $requirement->isValueValid($requirementsValues[$requirementName])) {
throw new TaxEngineException('Requirement value for `' . $requirementName . '` does not match required type', TaxEngineException::TAX_TYPE_BAD_REQUIREMENT_VALUE);
}
if (!$requirementType->isValid($requirementsValues[$requirement])) {
throw new TaxEngineException('Requirement value for `' . $requirement . '` does not match required type', TaxEngineException::TAX_TYPE_BAD_REQUIREMENT_VALUE);
}
$this->requirements[$requirement] = $requirementsValues[$requirement];
$this->requirements[$requirementName] = $requirementsValues[$requirementName];
}
}
public function setRequirement($key, $value) {
$this->requirements[$key] = $value;
return $this;
}
public function getRequirement($key)
{
if ($this->requirements === null) {
throw new TaxEngineException('Requirements are empty in BaseTaxType::getRequirement', TaxEngineException::UNDEFINED_REQUIREMENTS);
}
if (!array_key_exists($key, $this->requirements)) {
throw new TaxEngineException('Requirement value for `' . $key . '` does not exists in BaseTaxType::$requirements', TaxEngineException::UNDEFINED_REQUIREMENT_VALUE);
}
return $this->requirements[$key];
}
}
}

View File

@@ -25,74 +25,94 @@ namespace Thelia\TaxEngine;
use Symfony\Component\HttpFoundation\Session\Session;
use Thelia\Model\AddressQuery;
use Thelia\Model\CountryQuery;
use Thelia\Core\HttpFoundation\Request;
/**
* Class TaxEngine
*
* @package Thelia\TaxEngine
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class TaxEngine
{
protected static $instance = null;
protected $taxCountry = null;
protected $typeList = null;
protected static $taxCountry = null;
protected $taxTypesDirectories = array();
/**
* @var Session $session
*/
protected $session = null;
public static function getInstance(Session $session = null)
public function __construct(Request $request)
{
if (null === self::$instance) {
self::$instance = new TaxEngine();
}
$this->session = $request->getSession();
if (null !== self::$instance) {
self::$instance->setSession($session);
}
return self::$instance;
// Intialize the defaults Tax Types
$this->taxTypesDirectories['Thelia\\TaxEngine\\TaxType'] = __DIR__ . DS . "TaxType";
}
protected function setSession(Session $session)
{
$this->session = $session;
/**
* Add a directroy which contains tax types classes. The tax engine
* will scan this directory, and add all the tax type classes.
*
* @param unknown $namespace the namespace of the classes in the directory
* @param unknown $path_to_tax_type_classes the path to the directory
*/
public function addTaxTypeDirectory($namespace, $path_to_tax_type_classes) {
$this->taxTypesDirectories[$namespace] = $path_to_tax_type_classes;
}
private function getTaxTypeDirectory()
{
return __DIR__ . "/TaxType";
/**
* Add a tax type to the current list.
*
* @param unknown $fullyQualifiedclassName the fully qualified classname, su chas MyTaxes\Taxes\MyTaxType
*
*/
public function addTaxType($fullyQualifiedclassName) {
$this->typeList[] = $fullyQualifiedclassName;
}
public function getTaxTypeList()
{
$typeList = array();
if ($this->typeList === null) {
try {
$directoryBrowser = new \DirectoryIterator($this->getTaxTypeDirectory($this->getTaxTypeDirectory()));
} catch (\UnexpectedValueException $e) {
return $typeList;
$this->typeList = array();
foreach($this->taxTypesDirectories as $namespace => $directory) {
try {
$directoryIterator = new \DirectoryIterator($directory);
foreach ($directoryIterator as $fileinfo) {
if ($fileinfo->isFile()) {
$fileName = $fileinfo->getFilename();
$className = substr($fileName, 0, (1+strlen($fileinfo->getExtension())) * -1);
try {
$fullyQualifiedClassName = "$namespace\\$className";
$instance = new $fullyQualifiedClassName;
if ($instance instanceof BaseTaxType) {
$this->addTaxType(get_class($instance));
}
}
catch (\Exception $ex) {
// Nothing special to do
}
}
}
} catch (\UnexpectedValueException $e) {
// Nothing special to do
}
}
}
/* browse the directory */
foreach ($directoryBrowser as $directoryContent) {
/* is it a file ? */
if (!$directoryContent->isFile()) {
continue;
}
$fileName = $directoryContent->getFilename();
$className = substr($fileName, 0, (1+strlen($directoryContent->getExtension())) * -1);
if ($className == "BaseTaxType") {
continue;
}
$typeList[] = $className;
}
return $typeList;
return $this->typeList;
}
/**
@@ -100,30 +120,30 @@ class TaxEngine
* First look for a picked delivery address country
* Then look at the current customer default address country
* Else look at the default website country
*
* @param bool $force result is static cached ; even if a below parameter change between 2 calls, we need to keep coherent results. but you can force it.
* @return null|TaxEngine
*/
public function getDeliveryCountry($force = false)
public function getDeliveryCountry()
{
if (false === $force || null === self::$taxCountry) {
if (null === $this->taxCountry) {
/* is there a logged in customer ? */
if (null !== $customer = $this->session->getCustomerUser()) {
if (null !== $this->session->getOrder()
&& null !== $this->session->getOrder()->chosenDeliveryAddress
&& null !== $currentDeliveryAddress = AddressQuery::create()->findPk($this->session->getOrder()->chosenDeliveryAddress)) {
$taxCountry = $currentDeliveryAddress->getCountry();
$this->taxCountry = $currentDeliveryAddress->getCountry();
} else {
$customerDefaultAddress = $customer->getDefaultAddress();
$taxCountry = $customerDefaultAddress->getCountry();
$this->taxCountry = $customerDefaultAddress->getCountry();
}
} else {
$taxCountry = CountryQuery::create()->findOneByByDefault(1);
}
self::$taxCountry = $taxCountry;
if (null == $this->taxCountry) {
$this->taxCountry = CountryQuery::create()->findOneByByDefault(1);
}
}
return self::$taxCountry;
return $this->taxCountry;
}
}
}

View File

@@ -27,6 +27,9 @@ use Thelia\Model\FeatureProductQuery;
use Thelia\Model\Product;
use Thelia\Type\FloatType;
use Thelia\Type\ModelValidIdType;
use Thelia\Core\Translation\Translator;
use Thelia\TaxEngine\BaseTaxType;
use Thelia\TaxEngine\TaxTypeRequirementDefinition;
/**
*
@@ -35,9 +38,10 @@ use Thelia\Type\ModelValidIdType;
*/
class FeatureFixAmountTaxType extends BaseTaxType
{
public function pricePercentRetriever()
{
return 0;
public function setFeature($featureId) {
$this->setRequirement('feature', $featureId);
return $this;
}
public function fixAmountRetriever(Product $product)
@@ -53,21 +57,24 @@ class FeatureFixAmountTaxType extends BaseTaxType
$testInt = new FloatType();
if (!$testInt->isValid($taxAmount)) {
throw new TaxEngineException('Feature value does not match FLOAT format', TaxEngineException::FEATURE_BAD_EXPECTED_VALUE);
throw new TaxEngineException(
Translator::getInstance()->trans('Feature value does not match FLOAT format'),
TaxEngineException::FEATURE_BAD_EXPECTED_VALUE
);
}
return $taxAmount;
}
public function getRequirementsList()
public function getRequirementsDefinition()
{
return array(
'feature' => new ModelValidIdType('Feature'),
new TaxTypeRequirementDefinition('feature', new ModelValidIdType('Feature'))
);
}
public function getTitle()
{
return "Fix amount Tax depending on a feature";
return Translator::getInstance()->trans("Constant amount found in one of the product's feature");
}
}

View File

@@ -23,6 +23,9 @@
namespace Thelia\TaxEngine\TaxType;
use Thelia\Type\FloatType;
use Thelia\Core\Translation\Translator;
use Thelia\TaxEngine\BaseTaxType;
use Thelia\TaxEngine\TaxTypeRequirementDefinition;
/**
*
@@ -31,9 +34,10 @@ use Thelia\Type\FloatType;
*/
class FixAmountTaxType extends BaseTaxType
{
public function pricePercentRetriever()
{
return 0;
public function setAmount($amount) {
$this->setRequirement('amount', $amount);
return $this;
}
public function fixAmountRetriever(\Thelia\Model\Product $product)
@@ -41,15 +45,15 @@ class FixAmountTaxType extends BaseTaxType
return $this->getRequirement("amount");
}
public function getRequirementsList()
public function getRequirementsDefinition()
{
return array(
'amount' => new FloatType(),
new TaxTypeRequirementDefinition('amount', new FloatType())
);
}
public function getTitle()
{
return "Fix amount Tax";
return Translator::getInstance()->trans("Constant amount");
}
}

View File

@@ -23,6 +23,9 @@
namespace Thelia\TaxEngine\TaxType;
use Thelia\Type\FloatType;
use Thelia\Core\Translation\Translator;
use Thelia\TaxEngine\TaxTypeRequirementDefinition;
use Thelia\TaxEngine\BaseTaxType;
/**
*
@@ -31,25 +34,26 @@ use Thelia\Type\FloatType;
*/
class PricePercentTaxType extends BaseTaxType
{
public function setPercentage($percent) {
$this->setRequirement('percent', $percent);
return $this;
}
public function pricePercentRetriever()
{
return ($this->getRequirement("percent") * 0.01);
}
public function fixAmountRetriever(\Thelia\Model\Product $product)
{
return 0;
}
public function getRequirementsList()
public function getRequirementsDefinition()
{
return array(
'percent' => new FloatType(),
new TaxTypeRequirementDefinition('percent', new FloatType())
);
}
public function getTitle()
{
return "Price % Tax";
return Translator::getInstance()->trans("Percentage of the product price");
}
}

View File

@@ -0,0 +1,69 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\TaxEngine;
use Thelia\Type\TypeInterface;
/**
* This class defines a Tax type requirement
*
* @author Franck Allimant <franck@cqfdev.fr>
*/
class TaxTypeRequirementDefinition
{
/**
* @var string The requirement name
*/
protected $name;
/**
* @var TypeInterface The requirement type
*/
protected $type;
/**
* Create a new Tax type requirement
*
* @param string $name the name of the requirement
* @param TypeInterface $type the type of the data
*/
public function __construct($name, TypeInterface $type)
{
$this->name = $name;
$this->type = $type;
}
public function getName()
{
return $this->name;
}
public function getType()
{
return $this->type;
}
public function isValueValid($value) {
return $this->type->isValid($value);
}
}