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

@@ -12,6 +12,7 @@
</parameters> </parameters>
<services> <services>
<!-- URL maganement --> <!-- URL maganement -->
<service id="thelia.url.manager" class="Thelia\Tools\URL"> <service id="thelia.url.manager" class="Thelia\Tools\URL">
<argument type="service" id="service_container" /> <argument type="service" id="service_container" />
@@ -35,6 +36,13 @@
<argument type="service" id="esi" /> <argument type="service" id="esi" />
<argument type="service" id="fragment.renderer.inline" /> <argument type="service" id="fragment.renderer.inline" />
</service> </service>
<!-- Tax engine -->
<service id="thelia.taxEngine" class="Thelia\TaxEngine\TaxEngine" scope="request">
<argument type="service" id="request" />
</service>
<!-- <!--
A ControllerResolver that supports "a:b:c", "service:method" and class::method" notations, A ControllerResolver that supports "a:b:c", "service:method" and class::method" notations,
thus allowing the definition of controllers as service (see http://symfony.com/fr/doc/current/cookbook/controller/service.html) thus allowing the definition of controllers as service (see http://symfony.com/fr/doc/current/cookbook/controller/service.html)

View File

@@ -79,6 +79,7 @@
<tag name="thelia.parser.register_plugin"/> <tag name="thelia.parser.register_plugin"/>
<argument type="service" id="request" /> <argument type="service" id="request" />
<argument type="service" id="thelia.securityContext" /> <argument type="service" id="thelia.securityContext" />
<argument type="service" id="thelia.taxEngine" />
<argument type="service" id="thelia.parser.context"/> <argument type="service" id="thelia.parser.context"/>
<argument type="service" id="event_dispatcher"/> <argument type="service" id="event_dispatcher"/>
</service> </service>

View File

@@ -29,6 +29,7 @@ use Thelia\Core\Event\TheliaEvents;
use Thelia\Form\TaxCreationForm; use Thelia\Form\TaxCreationForm;
use Thelia\Form\TaxModificationForm; use Thelia\Form\TaxModificationForm;
use Thelia\Model\TaxQuery; use Thelia\Model\TaxQuery;
use Thelia\Model\Tax;
class TaxController extends AbstractCrudController class TaxController extends AbstractCrudController
{ {
@@ -49,7 +50,9 @@ class TaxController extends AbstractCrudController
protected function getCreationForm() protected function getCreationForm()
{ {
return new TaxCreationForm($this->getRequest()); $form = new TaxCreationForm($this->getRequest());
return $form;
} }
protected function getUpdateForm() protected function getUpdateForm()
@@ -64,7 +67,7 @@ class TaxController extends AbstractCrudController
$event->setLocale($formData['locale']); $event->setLocale($formData['locale']);
$event->setTitle($formData['title']); $event->setTitle($formData['title']);
$event->setDescription($formData['description']); $event->setDescription($formData['description']);
$event->setType($formData['type']); $event->setType(Tax::unescapeTypeName($formData['type']));
$event->setRequirements($this->getRequirements($formData['type'], $formData)); $event->setRequirements($this->getRequirements($formData['type'], $formData));
return $event; return $event;
@@ -78,7 +81,7 @@ class TaxController extends AbstractCrudController
$event->setId($formData['id']); $event->setId($formData['id']);
$event->setTitle($formData['title']); $event->setTitle($formData['title']);
$event->setDescription($formData['description']); $event->setDescription($formData['description']);
$event->setType($formData['type']); $event->setType(Tax::unescapeTypeName($formData['type']));
$event->setRequirements($this->getRequirements($formData['type'], $formData)); $event->setRequirements($this->getRequirements($formData['type'], $formData));
return $event; return $event;
@@ -107,7 +110,7 @@ class TaxController extends AbstractCrudController
'locale' => $object->getLocale(), 'locale' => $object->getLocale(),
'title' => $object->getTitle(), 'title' => $object->getTitle(),
'description' => $object->getDescription(), 'description' => $object->getDescription(),
'type' => $object->getType(), 'type' => Tax::escapeTypeName($object->getType()),
); );
// Setup the object form // Setup the object form
@@ -200,27 +203,20 @@ class TaxController extends AbstractCrudController
); );
} }
protected function checkRequirements($formData)
{
$type = $formData['type'];
}
protected function getRequirements($type, $formData) protected function getRequirements($type, $formData)
{ {
$requirements = array(); $requirements = array();
foreach ($formData as $data => $value) { foreach ($formData as $data => $value) {
if (!strstr($data, ':')) { if (!strstr($data, ':')) {
continue; continue;
} }
$couple = explode(':', $data); $couple = explode(':', $data);
if (count($couple) != 2 || $couple[0] != $type) { if (count($couple) == 2 && $couple[0] == $type) {
continue; $requirements[$couple[1]] = $value;
} }
$requirements[$couple[1]] = $value;
} }
return $requirements; return $requirements;

View File

@@ -78,7 +78,7 @@ class Cart extends BaseLoop implements ArraySearchLoopInterface
public function parseResults(LoopResult $loopResult) public function parseResults(LoopResult $loopResult)
{ {
$taxCountry = TaxEngine::getInstance($this->request->getSession())->getDeliveryCountry(); $taxCountry = $this->container->get('thelia.taxEngine')->getDeliveryCountry();
foreach ($loopResult->getResultDataCollection() as $cartItem) { foreach ($loopResult->getResultDataCollection() as $cartItem) {
$product = $cartItem->getProduct(); $product = $cartItem->getProduct();

View File

@@ -60,7 +60,7 @@ class Delivery extends BaseSpecificModule
throw new \InvalidArgumentException('Cannot found country id: `' . $countryId . '` in delivery loop'); throw new \InvalidArgumentException('Cannot found country id: `' . $countryId . '` in delivery loop');
} }
} else { } else {
$country = TaxEngine::getInstance($this->request->getSession())->getDeliveryCountry(); $country = $this->container->get('thelia.taxEngine')->getDeliveryCountry();
} }
foreach ($loopResult->getResultDataCollection() as $deliveryModule) { foreach ($loopResult->getResultDataCollection() as $deliveryModule) {

View File

@@ -464,7 +464,7 @@ class Product extends BaseI18nLoop implements PropelSearchLoopInterface, SearchL
return $this->parseComplex($loopResult); return $this->parseComplex($loopResult);
} }
$taxCountry = TaxEngine::getInstance($this->request->getSession())->getDeliveryCountry(); $taxCountry = $this->container->get('thelia.taxEngine')->getDeliveryCountry();
foreach ($loopResult->getResultDataCollection() as $product) { foreach ($loopResult->getResultDataCollection() as $product) {
@@ -982,7 +982,7 @@ class Product extends BaseI18nLoop implements PropelSearchLoopInterface, SearchL
{ {
$loopResult = new LoopResult($results); $loopResult = new LoopResult($results);
$taxCountry = TaxEngine::getInstance($this->request->getSession())->getDeliveryCountry(); $taxCountry = $this->container->get('thelia.taxEngine')->getDeliveryCountry();
foreach ($loopResult->getResultDataCollection() as $product) { foreach ($loopResult->getResultDataCollection() as $product) {

View File

@@ -145,7 +145,7 @@ class ProductSaleElements extends BaseLoop implements PropelSearchLoopInterface
public function parseResults(LoopResult $loopResult) public function parseResults(LoopResult $loopResult)
{ {
$taxCountry = TaxEngine::getInstance($this->request->getSession())->getDeliveryCountry(); $taxCountry = $this->container->get('thelia.taxEngine')->getDeliveryCountry();
foreach ($loopResult->getResultDataCollection() as $PSEValue) { foreach ($loopResult->getResultDataCollection() as $PSEValue) {
$loopResultRow = new LoopResultRow($PSEValue); $loopResultRow = new LoopResultRow($PSEValue);

View File

@@ -150,6 +150,7 @@ class Tax extends BaseI18nLoop implements PropelSearchLoopInterface
$loopResultRow $loopResultRow
->set("ID" , $tax->getId()) ->set("ID" , $tax->getId())
->set("TYPE" , $tax->getType()) ->set("TYPE" , $tax->getType())
->set("ESCAPED_TYPE" , \Thelia\Model\Tax::escapeTypeName($tax->getType()))
->set("REQUIREMENTS" , $tax->getRequirements()) ->set("REQUIREMENTS" , $tax->getRequirements())
->set("IS_TRANSLATED" , $tax->getVirtualColumn('IS_TRANSLATED')) ->set("IS_TRANSLATED" , $tax->getVirtualColumn('IS_TRANSLATED'))
->set("LOCALE" , $this->locale) ->set("LOCALE" , $this->locale)

View File

@@ -58,15 +58,17 @@ class DataAccessFunctions extends AbstractSmartyPlugin
protected $parserContext; protected $parserContext;
protected $request; protected $request;
protected $dispatcher; protected $dispatcher;
protected $taxEngine;
private static $dataAccessCache = array(); private static $dataAccessCache = array();
public function __construct(Request $request, SecurityContext $securityContext, ParserContext $parserContext, ContainerAwareEventDispatcher $dispatcher) public function __construct(Request $request, SecurityContext $securityContext, TaxEngine $taxEngine, ParserContext $parserContext, ContainerAwareEventDispatcher $dispatcher)
{ {
$this->securityContext = $securityContext; $this->securityContext = $securityContext;
$this->parserContext = $parserContext; $this->parserContext = $parserContext;
$this->request = $request; $this->request = $request;
$this->dispatcher = $dispatcher; $this->dispatcher = $dispatcher;
$this->taxEngine = $taxEngine;
} }
/** /**
@@ -184,7 +186,7 @@ class DataAccessFunctions extends AbstractSmartyPlugin
if (array_key_exists('currentCountry', self::$dataAccessCache)) { if (array_key_exists('currentCountry', self::$dataAccessCache)) {
$taxCountry = self::$dataAccessCache['currentCountry']; $taxCountry = self::$dataAccessCache['currentCountry'];
} else { } else {
$taxCountry = TaxEngine::getInstance($this->request->getSession())->getDeliveryCountry(); $taxCountry = $this->taxEngine->getDeliveryCountry();
self::$dataAccessCache['currentCountry'] = $taxCountry; self::$dataAccessCache['currentCountry'] = $taxCountry;
} }
@@ -378,12 +380,17 @@ class DataAccessFunctions extends AbstractSmartyPlugin
self::$dataAccessCache['data_' . $objectLabel] = $data; self::$dataAccessCache['data_' . $objectLabel] = $data;
} }
$noGetterData = array(); if ($data !== null) {
foreach ($columns as $column) { $noGetterData = array();
$noGetterData[$column] = $data->getVirtualColumn('i18n_' . $column);
foreach ($columns as $column) {
$noGetterData[$column] = $data->getVirtualColumn('i18n_' . $column);
}
return $this->dataAccess($objectLabel, $params, $data, $noGetterData);
} }
return $this->dataAccess($objectLabel, $params, $data, $noGetterData); return '';
} }
/** /**

View File

@@ -28,6 +28,8 @@ use Thelia\Core\Form\Type\TheliaType;
use Thelia\Core\Translation\Translator; use Thelia\Core\Translation\Translator;
use Thelia\TaxEngine\TaxEngine; use Thelia\TaxEngine\TaxEngine;
use Thelia\TaxEngine\TaxType; use Thelia\TaxEngine\TaxType;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Model\Tax;
/** /**
* Class TaxCreationForm * Class TaxCreationForm
@@ -40,14 +42,21 @@ class TaxCreationForm extends BaseForm
protected function buildForm($change_mode = false) protected function buildForm($change_mode = false)
{ {
$types = TaxEngine::getInstance($this->getRequest()->getSession())->getTaxTypeList(); // FIXME : SHOULD be extracted from the container
$taxEngine = new TaxEngine($this->getRequest());
$types = $taxEngine->getTaxTypeList();
$typeList = array(); $typeList = array();
$requirementList = array(); $requirementList = array();
foreach ($types as $type) {
$classPath = "\\Thelia\\TaxEngine\\TaxType\\$type"; foreach ($types as $classname) {
$instance = new $classPath();
$typeList[$type] = $instance->getTitle(); $instance = new $classname();
$requirementList[$type] = $instance->getRequirementsList();
$typeList[Tax::escapeTypeName($classname)] = $instance->getTitle();
$requirementList[$classname] = $instance->getRequirementsDefinition();
} }
$this->formBuilder $this->formBuilder
@@ -65,27 +74,29 @@ class TaxCreationForm extends BaseForm
)) ))
; ;
foreach ($requirementList as $type => $requirements) { foreach ($requirementList as $name => $requirements) {
foreach ($requirements as $name => $requirementType) { foreach ($requirements as $requirement) {
$this->formBuilder $this->formBuilder
->add($type . ':' . $name, new TheliaType(), array( // Replace the '\' in the class name by hyphens
//"instance" => $requirementType, // See TaxController::getRequirements if some changes are made about this.
->add(Tax::escapeTypeName($name) . ':' . $requirement->getName(), new TheliaType(), array(
//"instance" => $requirement->getType(),
"constraints" => array( "constraints" => array(
new Constraints\Callback( new Constraints\Callback(
array( array(
"methods" => array( "methods" => array(
array($requirementType, "verifyForm"), array($requirement->getType(), "verifyForm"),
), ),
) )
), ),
), ),
"attr" => array( "attr" => array(
"tag" => "requirements", "tag" => "requirements",
"tax_type" => $type, "tax_type" => Tax::escapeTypeName($name),
), ),
"label" => Translator::getInstance()->trans($name), "label" => Translator::getInstance()->trans($requirement->getName()),
"type" => $requirementType->getFormType(), "type" => $requirement->getType()->getFormType(),
"options" => $requirementType->getFormOptions(), "options" => $requirement->getType()->getFormOptions(),
)) ))
; ;
} }

View File

@@ -5,12 +5,23 @@ namespace Thelia\Model;
use Thelia\Exception\TaxEngineException; use Thelia\Exception\TaxEngineException;
use Thelia\Model\Base\Tax as BaseTax; use Thelia\Model\Base\Tax as BaseTax;
use Thelia\Model\Tools\ModelEventDispatcherTrait; use Thelia\Model\Tools\ModelEventDispatcherTrait;
use Thelia\TaxEngine\TaxType\BaseTaxType; use Thelia\TaxEngine\BaseTaxType;
class Tax extends BaseTax class Tax extends BaseTax
{ {
use ModelEventDispatcherTrait; use ModelEventDispatcherTrait;
/**
* Provides a form-and-javascript-safe version of the type, which is a fully qualified classname, with \
*/
public static function escapeTypeName($name) {
return str_replace('\\', '-', $name);
}
public static function unescapeTypeName($name) {
return str_replace('-', '\\', $name);
}
public function calculateTax($amount) public function calculateTax($amount)
{ {
if(false === filter_var($amount, FILTER_VALIDATE_FLOAT)) { if(false === filter_var($amount, FILTER_VALIDATE_FLOAT)) {
@@ -39,7 +50,7 @@ class Tax extends BaseTax
public function getTypeInstance() public function getTypeInstance()
{ {
$class = '\\Thelia\\TaxEngine\\TaxType\\' . $this->getType(); $class = $this->getType();
/* test type */ /* test type */
if(!class_exists($class)) { if(!class_exists($class)) {

View File

@@ -20,7 +20,7 @@
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* 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\Exception\TaxEngineException;
use Thelia\Model\Product; use Thelia\Model\Product;
@@ -33,14 +33,54 @@ use Thelia\Type\TypeInterface;
*/ */
abstract class BaseTaxType 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(); abstract public function getTitle();
public function calculate(Product $product, $untaxedPrice) public function calculate(Product $product, $untaxedPrice)
@@ -48,37 +88,46 @@ abstract class BaseTaxType
return $untaxedPrice * $this->pricePercentRetriever() + $this->fixAmountRetriever($product); 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) public function loadRequirements($requirementsValues)
{ {
$this->requirements = $this->getRequirementsList(); $requirements = $this->getRequirementsDefinition();
if (!is_array($this->requirements)) { if (!is_array($requirements)) {
throw new TaxEngineException('getRequirementsList must return an array', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD); throw new TaxEngineException('getRequirementsDefinition must return an array', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD);
} }
foreach ($this->requirements as $requirement => $requirementType) { foreach ($requirements as $requirement) {
if (!$requirementType instanceof TypeInterface) {
throw new TaxEngineException('getRequirementsList must return an array of TypeInterface', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD); $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)) { if (! $requirement->isValueValid($requirementsValues[$requirementName])) {
throw new TaxEngineException('Cannot load requirements : requirement value for `' . $requirement . '` not found', TaxEngineException::TAX_TYPE_REQUIREMENT_NOT_FOUND); throw new TaxEngineException('Requirement value for `' . $requirementName . '` does not match required type', TaxEngineException::TAX_TYPE_BAD_REQUIREMENT_VALUE);
} }
if (!$requirementType->isValid($requirementsValues[$requirement])) { $this->requirements[$requirementName] = $requirementsValues[$requirementName];
throw new TaxEngineException('Requirement value for `' . $requirement . '` does not match required type', TaxEngineException::TAX_TYPE_BAD_REQUIREMENT_VALUE);
}
$this->requirements[$requirement] = $requirementsValues[$requirement];
} }
} }
public function setRequirement($key, $value) {
$this->requirements[$key] = $value;
return $this;
}
public function getRequirement($key) 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)) { if (!array_key_exists($key, $this->requirements)) {
throw new TaxEngineException('Requirement value for `' . $key . '` does not exists in BaseTaxType::$requirements', TaxEngineException::UNDEFINED_REQUIREMENT_VALUE); throw new TaxEngineException('Requirement value for `' . $key . '` does not exists in BaseTaxType::$requirements', TaxEngineException::UNDEFINED_REQUIREMENT_VALUE);
} }

View File

@@ -25,74 +25,94 @@ namespace Thelia\TaxEngine;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Thelia\Model\AddressQuery; use Thelia\Model\AddressQuery;
use Thelia\Model\CountryQuery; use Thelia\Model\CountryQuery;
use Thelia\Core\HttpFoundation\Request;
/** /**
* Class TaxEngine * Class TaxEngine
*
* @package Thelia\TaxEngine * @package Thelia\TaxEngine
* @author Etienne Roudeix <eroudeix@openstudio.fr> * @author Etienne Roudeix <eroudeix@openstudio.fr>
*/ */
class TaxEngine class TaxEngine
{ {
protected static $instance = null; protected $taxCountry = null;
protected $typeList = null;
protected static $taxCountry = null; protected $taxTypesDirectories = array();
/** /**
* @var Session $session * @var Session $session
*/ */
protected $session = null; protected $session = null;
public static function getInstance(Session $session = null) public function __construct(Request $request)
{ {
if (null === self::$instance) { $this->session = $request->getSession();
self::$instance = new TaxEngine();
}
if (null !== self::$instance) { // Intialize the defaults Tax Types
self::$instance->setSession($session); $this->taxTypesDirectories['Thelia\\TaxEngine\\TaxType'] = __DIR__ . DS . "TaxType";
}
return self::$instance;
} }
protected function setSession(Session $session) /**
{ * Add a directroy which contains tax types classes. The tax engine
$this->session = $session; * 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() /**
{ * Add a tax type to the current list.
return __DIR__ . "/TaxType"; *
* @param unknown $fullyQualifiedclassName the fully qualified classname, su chas MyTaxes\Taxes\MyTaxType
*
*/
public function addTaxType($fullyQualifiedclassName) {
$this->typeList[] = $fullyQualifiedclassName;
} }
public function getTaxTypeList() public function getTaxTypeList()
{ {
$typeList = array(); if ($this->typeList === null) {
try { $this->typeList = array();
$directoryBrowser = new \DirectoryIterator($this->getTaxTypeDirectory($this->getTaxTypeDirectory()));
} catch (\UnexpectedValueException $e) { foreach($this->taxTypesDirectories as $namespace => $directory) {
return $typeList;
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 */ return $this->typeList;
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;
} }
/** /**
@@ -100,30 +120,30 @@ class TaxEngine
* First look for a picked delivery address country * First look for a picked delivery address country
* Then look at the current customer default address country * Then look at the current customer default address country
* Else look at the default website 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 * @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 ? */ /* is there a logged in customer ? */
if (null !== $customer = $this->session->getCustomerUser()) { if (null !== $customer = $this->session->getCustomerUser()) {
if (null !== $this->session->getOrder() if (null !== $this->session->getOrder()
&& null !== $this->session->getOrder()->chosenDeliveryAddress && null !== $this->session->getOrder()->chosenDeliveryAddress
&& null !== $currentDeliveryAddress = AddressQuery::create()->findPk($this->session->getOrder()->chosenDeliveryAddress)) { && null !== $currentDeliveryAddress = AddressQuery::create()->findPk($this->session->getOrder()->chosenDeliveryAddress)) {
$taxCountry = $currentDeliveryAddress->getCountry(); $this->taxCountry = $currentDeliveryAddress->getCountry();
} else { } else {
$customerDefaultAddress = $customer->getDefaultAddress(); $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\Model\Product;
use Thelia\Type\FloatType; use Thelia\Type\FloatType;
use Thelia\Type\ModelValidIdType; 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 class FeatureFixAmountTaxType extends BaseTaxType
{ {
public function pricePercentRetriever() public function setFeature($featureId) {
{ $this->setRequirement('feature', $featureId);
return 0;
return $this;
} }
public function fixAmountRetriever(Product $product) public function fixAmountRetriever(Product $product)
@@ -53,21 +57,24 @@ class FeatureFixAmountTaxType extends BaseTaxType
$testInt = new FloatType(); $testInt = new FloatType();
if (!$testInt->isValid($taxAmount)) { 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; return $taxAmount;
} }
public function getRequirementsList() public function getRequirementsDefinition()
{ {
return array( return array(
'feature' => new ModelValidIdType('Feature'), new TaxTypeRequirementDefinition('feature', new ModelValidIdType('Feature'))
); );
} }
public function getTitle() 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; namespace Thelia\TaxEngine\TaxType;
use Thelia\Type\FloatType; 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 class FixAmountTaxType extends BaseTaxType
{ {
public function pricePercentRetriever() public function setAmount($amount) {
{ $this->setRequirement('amount', $amount);
return 0;
return $this;
} }
public function fixAmountRetriever(\Thelia\Model\Product $product) public function fixAmountRetriever(\Thelia\Model\Product $product)
@@ -41,15 +45,15 @@ class FixAmountTaxType extends BaseTaxType
return $this->getRequirement("amount"); return $this->getRequirement("amount");
} }
public function getRequirementsList() public function getRequirementsDefinition()
{ {
return array( return array(
'amount' => new FloatType(), new TaxTypeRequirementDefinition('amount', new FloatType())
); );
} }
public function getTitle() public function getTitle()
{ {
return "Fix amount Tax"; return Translator::getInstance()->trans("Constant amount");
} }
} }

View File

@@ -23,6 +23,9 @@
namespace Thelia\TaxEngine\TaxType; namespace Thelia\TaxEngine\TaxType;
use Thelia\Type\FloatType; 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 class PricePercentTaxType extends BaseTaxType
{ {
public function setPercentage($percent) {
$this->setRequirement('percent', $percent);
return $this;
}
public function pricePercentRetriever() public function pricePercentRetriever()
{ {
return ($this->getRequirement("percent") * 0.01); return ($this->getRequirement("percent") * 0.01);
} }
public function fixAmountRetriever(\Thelia\Model\Product $product) public function getRequirementsDefinition()
{
return 0;
}
public function getRequirementsList()
{ {
return array( return array(
'percent' => new FloatType(), new TaxTypeRequirementDefinition('percent', new FloatType())
); );
} }
public function getTitle() 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);
}
}

View File

@@ -30,6 +30,7 @@ use Thelia\Core\Security\SecurityContext;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Thelia\Core\HttpFoundation\Session\Session; use Thelia\Core\HttpFoundation\Session\Session;
use Thelia\Tools\URL; use Thelia\Tools\URL;
use Thelia\TaxEngine\TaxEngine;
/** /**
* *
@@ -108,6 +109,7 @@ abstract class BaseLoopTestor extends \PHPUnit_Framework_TestCase
$this->container->set('thelia.securityContext', new SecurityContext($request)); $this->container->set('thelia.securityContext', new SecurityContext($request));
$this->container->set('router.admin', $stubRouterAdmin); $this->container->set('router.admin', $stubRouterAdmin);
$this->container->set('thelia.url.manager', new URL($this->container)); $this->container->set('thelia.url.manager', new URL($this->container));
$this->container->set('thelia.taxEngine', new TaxEngine($request));
$this->instance = $this->getTestedInstance(); $this->instance = $this->getTestedInstance();
$this->instance->initializeArgs($this->getMandatoryArguments()); $this->instance->initializeArgs($this->getMandatoryArguments());

View File

@@ -190,25 +190,25 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase
$taxRulesCollection->setModel('\Thelia\Model\Tax'); $taxRulesCollection->setModel('\Thelia\Model\Tax');
$tax = new Tax(); $tax = new Tax();
$tax->setType('PricePercentTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\PricePercentTaxType')
->setRequirements(array('percent' => 10)) ->setRequirements(array('percent' => 10))
->setVirtualColumn('taxRuleCountryPosition', 1); ->setVirtualColumn('taxRuleCountryPosition', 1);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);
$tax = new Tax(); $tax = new Tax();
$tax->setType('PricePercentTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\PricePercentTaxType')
->setRequirements(array('percent' => 8)) ->setRequirements(array('percent' => 8))
->setVirtualColumn('taxRuleCountryPosition', 1); ->setVirtualColumn('taxRuleCountryPosition', 1);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);
$tax = new Tax(); $tax = new Tax();
$tax->setType('FixAmountTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\FixAmountTaxType')
->setRequirements(array('amount' => 5)) ->setRequirements(array('amount' => 5))
->setVirtualColumn('taxRuleCountryPosition', 2); ->setVirtualColumn('taxRuleCountryPosition', 2);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);
$tax = new Tax(); $tax = new Tax();
$tax->setType('PricePercentTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\PricePercentTaxType')
->setRequirements(array('percent' => 1)) ->setRequirements(array('percent' => 1))
->setVirtualColumn('taxRuleCountryPosition', 3); ->setVirtualColumn('taxRuleCountryPosition', 3);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);
@@ -246,25 +246,25 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase
$taxRulesCollection->setModel('\Thelia\Model\Tax'); $taxRulesCollection->setModel('\Thelia\Model\Tax');
$tax = new Tax(); $tax = new Tax();
$tax->setType('PricePercentTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\PricePercentTaxType')
->setRequirements(array('percent' => 10)) ->setRequirements(array('percent' => 10))
->setVirtualColumn('taxRuleCountryPosition', 1); ->setVirtualColumn('taxRuleCountryPosition', 1);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);
$tax = new Tax(); $tax = new Tax();
$tax->setType('PricePercentTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\PricePercentTaxType')
->setRequirements(array('percent' => 8)) ->setRequirements(array('percent' => 8))
->setVirtualColumn('taxRuleCountryPosition', 1); ->setVirtualColumn('taxRuleCountryPosition', 1);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);
$tax = new Tax(); $tax = new Tax();
$tax->setType('FixAmountTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\FixAmountTaxType')
->setRequirements(array('amount' => 5)) ->setRequirements(array('amount' => 5))
->setVirtualColumn('taxRuleCountryPosition', 2); ->setVirtualColumn('taxRuleCountryPosition', 2);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);
$tax = new Tax(); $tax = new Tax();
$tax->setType('PricePercentTaxType') $tax->setType('\Thelia\TaxEngine\TaxType\PricePercentTaxType')
->setRequirements(array('percent' => 1)) ->setRequirements(array('percent' => 1))
->setVirtualColumn('taxRuleCountryPosition', 3); ->setVirtualColumn('taxRuleCountryPosition', 3);
$taxRulesCollection->append($tax); $taxRulesCollection->append($tax);

View File

@@ -0,0 +1,61 @@
<?php
use Thelia\TaxEngine\TaxEngine;
/*************************************************************************************/
/* */
/* 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\Tests\TaxEngine;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Thelia\TaxEngine\TaxEngine;
/**
*
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
class TaxEngineTest extends \PHPUnit_Framework_TestCase
{
protected $request;
public function setUp()
{
$this->request = new Request();
$this->request->setSession(new Session(new MockArraySessionStorage()));
}
/**
*/
public function testGetTaxTypeList()
{
$taxEngine = new TaxEngine($this->request);
$list = $taxEngine->getTaxTypeList();
$this->assertEquals($list[0], "Thelia\TaxEngine\TaxType\FeatureFixAmountTaxType");
$this->assertEquals($list[1], "Thelia\TaxEngine\TaxType\FixAmountTaxType");
$this->assertEquals($list[2], "Thelia\TaxEngine\TaxType\PricePercentTaxType");
}
}

View File

@@ -1193,8 +1193,8 @@ INSERT INTO `country_i18n` (`id`, `locale`, `title`, `description`, `chapo`, `po
INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `created_at`, `updated_at`) INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `created_at`, `updated_at`)
VALUES VALUES
(1, 'PricePercentTaxType', 'eyJwZXJjZW50IjoiMjAifQ==', NOW(), NOW()), (1, 'Thelia\\TaxEngine\\TaxType\\PricePercentTaxType', 'eyJwZXJjZW50IjoiMjAifQ==', NOW(), NOW()),
(2, 'PricePercentTaxType', 'eyJwZXJjZW50IjoiMTAifQ==', NOW(), NOW()); (2, 'Thelia\\TaxEngine\\TaxType\\PricePercentTaxType', 'eyJwZXJjZW50IjoiMTAifQ==', NOW(), NOW());
INSERT INTO `tax_i18n` (`id`, `locale`, `title`) INSERT INTO `tax_i18n` (`id`, `locale`, `title`)
VALUES VALUES

View File

@@ -6,6 +6,16 @@
</caption> </caption>
<thead> <thead>
<tr> <tr>
<th>
{admin_sortable_header
current_order=$module_order
order='id'
reverse_order='id_reverse'
path={url path='/admin/modules'}
request_parameter_name='module_order'
label="{intl l='ID'}"
}
</th>
<th> <th>
{admin_sortable_header {admin_sortable_header
current_order=$module_order current_order=$module_order
@@ -70,6 +80,7 @@
<tbody> <tbody>
{loop type="module" name="module.{$module_type}" module_type={$module_type|default:1} order=$module_order backend_context=1} {loop type="module" name="module.{$module_type}" module_type={$module_type|default:1} order=$module_order backend_context=1}
<tr> <tr>
<td>{$ID}</td>
<td>{$TITLE}</td> <td>{$TITLE}</td>
<td>{$CODE}</td> <td>{$CODE}</td>
<td>{$CHAPO}</td> <td>{$CHAPO}</td>
@@ -103,7 +114,7 @@
<td class="text-right"> <td class="text-right">
<div class="btn-group"> <div class="btn-group">
{if $CONFIGURABLE == 1} C:{if $CONFIGURABLE == 1}1
{loop type="auth" name="can_change" role="ADMIN" module=$CODE access="VIEW"} {loop type="auth" name="can_change" role="ADMIN" module=$CODE access="VIEW"}
<a class="{if ! $ACTIVE}disabled {/if} btn btn-primary btn-xs" id="config-btn-{$ID}" title="{intl l='Configure this module'}" href="{url path="/admin/module/$CODE"}">{intl l="Configure"}</a> <a class="{if ! $ACTIVE}disabled {/if} btn btn-primary btn-xs" id="config-btn-{$ID}" title="{intl l='Configure this module'}" href="{url path="/admin/module/$CODE"}">{intl l="Configure"}</a>
{/loop} {/loop}

View File

@@ -79,7 +79,7 @@
<div class="form-group"> <div class="form-group">
<select name="{$name}" class="js-change-tax-type" data-toggle="selectpicker"> <select name="{$name}" class="js-change-tax-type" data-toggle="selectpicker">
{foreach $choices as $choice} {foreach $choices as $choice}
<option value="{$choice->value}" {if !$form_error && $choice->value == $TYPE || $form_error && $choice->value == $value}selected="selected" {/if}>{$choice->label}</option> <option value="{$choice->value}" {if !$form_error && $choice->value == $ESCAPED_TYPE || $form_error && $choice->value == $value}selected="selected" {/if}>{$choice->label}</option>
{/foreach} {/foreach}
</select> </select>
</div> </div>
@@ -89,19 +89,19 @@
{/form_field} {/form_field}
{form_tagged_fields form=$form tag='requirements'} {form_tagged_fields form=$form tag='requirements'}
<div class="form-group {if $error}has-error{/if} js-tax-requirements" data-tax-type="{$attr_list.tax_type}" {if !$form_error && $attr_list.tax_type != $TYPE || $form_error && $attr_list.tax_type != $typeValue}style="display: none"{/if}> <div class="form-group {if $error}has-error{/if} js-tax-requirements" data-tax-type="{$attr_list.tax_type}" {if !$form_error && $attr_list.tax_type != $ESCAPED_TYPE || $form_error && $attr_list.tax_type != $typeValue}style="display: none"{/if}>
<label for="{$label_attr.for}" class="control-label"> <label for="{$label_attr.for}" class="control-label">
{intl l=$label} {intl l=$label}
</label> </label>
{if $formType == 'choice'} {if $formType == 'choice'}
<select name="{$name}" data-toggle="selectpicker" {if !$form_error && $attr_list.tax_type != $TYPE || $form_error && $attr_list.tax_type != $typeValue}disabled="disabled"{/if}> <select name="{$name}" data-toggle="selectpicker" {if !$form_error && $attr_list.tax_type != $ESCAPED_TYPE || $form_error && $attr_list.tax_type != $typeValue}disabled="disabled"{/if}>
{foreach $choices as $choice} {foreach $choices as $choice}
<option value="{$choice->value}" {if !$form_error && $REQUIREMENTS.$label == $choice->value || $form_error && $value == $choice->value}selected="selected" {/if}>{$choice->label}</option> <option value="{$choice->value}" {if !$form_error && $REQUIREMENTS.$label == $choice->value || $form_error && $value == $choice->value}selected="selected" {/if}>{$choice->label}</option>
{/foreach} {/foreach}
</select> </select>
{/if} {/if}
{if $formType == 'text'} {if $formType == 'text'}
<input type="text" {if !$form_error && $attr_list.tax_type != $TYPE || $form_error && $attr_list.tax_type != $typeValue}disabled="disabled"{/if} id="{$label_attr.for}" name="{$name}" required="required" class="form-control" value="{if $form_error}{$value}{else}{$REQUIREMENTS.$label}{/if}"> <input type="text" {if !$form_error && $attr_list.tax_type != $ESCAPED_TYPE || $form_error && $attr_list.tax_type != $typeValue}disabled="disabled"{/if} id="{$label_attr.for}" name="{$name}" required="required" class="form-control" value="{if $form_error}{$value}{else}{$REQUIREMENTS.$label}{/if}">
{/if} {/if}
</div> </div>
{/form_tagged_fields} {/form_tagged_fields}