- * $query->filterByRate(1234); // WHERE rate = 1234
- * $query->filterByRate(array(12, 34)); // WHERE rate IN (12, 34)
- * $query->filterByRate(array('min' => 12)); // WHERE rate > 12
+ * $query->filterByType('fooValue'); // WHERE type = 'fooValue'
+ * $query->filterByType('%fooValue%'); // WHERE type LIKE '%fooValue%'
*
*
- * @param mixed $rate The value to use as filter.
- * Use scalar values for equality.
- * Use array values for in_array() equivalent.
- * Use associative array('min' => $minValue, 'max' => $maxValue) for intervals.
+ * @param string $type The value to use as filter.
+ * Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
*
* @return ChildTaxQuery The current query, for fluid interface
*/
- public function filterByRate($rate = null, $comparison = null)
+ public function filterByType($type = null, $comparison = null)
{
- if (is_array($rate)) {
- $useMinMax = false;
- if (isset($rate['min'])) {
- $this->addUsingAlias(TaxTableMap::RATE, $rate['min'], Criteria::GREATER_EQUAL);
- $useMinMax = true;
- }
- if (isset($rate['max'])) {
- $this->addUsingAlias(TaxTableMap::RATE, $rate['max'], Criteria::LESS_EQUAL);
- $useMinMax = true;
- }
- if ($useMinMax) {
- return $this;
- }
- if (null === $comparison) {
+ if (null === $comparison) {
+ if (is_array($type)) {
$comparison = Criteria::IN;
+ } elseif (preg_match('/[\%\*]/', $type)) {
+ $type = str_replace('*', '%', $type);
+ $comparison = Criteria::LIKE;
}
}
- return $this->addUsingAlias(TaxTableMap::RATE, $rate, $comparison);
+ return $this->addUsingAlias(TaxTableMap::TYPE, $type, $comparison);
+ }
+
+ /**
+ * Filter the query on the serialized_requirements column
+ *
+ * Example usage:
+ *
+ * $query->filterBySerializedRequirements('fooValue'); // WHERE serialized_requirements = 'fooValue'
+ * $query->filterBySerializedRequirements('%fooValue%'); // WHERE serialized_requirements LIKE '%fooValue%'
+ *
+ *
+ * @param string $serializedRequirements The value to use as filter.
+ * Accepts wildcards (* and % trigger a LIKE)
+ * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
+ *
+ * @return ChildTaxQuery The current query, for fluid interface
+ */
+ public function filterBySerializedRequirements($serializedRequirements = null, $comparison = null)
+ {
+ if (null === $comparison) {
+ if (is_array($serializedRequirements)) {
+ $comparison = Criteria::IN;
+ } elseif (preg_match('/[\%\*]/', $serializedRequirements)) {
+ $serializedRequirements = str_replace('*', '%', $serializedRequirements);
+ $comparison = Criteria::LIKE;
+ }
+ }
+
+ return $this->addUsingAlias(TaxTableMap::SERIALIZED_REQUIREMENTS, $serializedRequirements, $comparison);
}
/**
diff --git a/core/lib/Thelia/Model/Coupon.php b/core/lib/Thelia/Model/Coupon.php
index f5788ff4f..7a7ce1e4a 100755
--- a/core/lib/Thelia/Model/Coupon.php
+++ b/core/lib/Thelia/Model/Coupon.php
@@ -49,24 +49,25 @@ class Coupon extends BaseCoupon
/**
- * Constructor
+ * Create or Update this Coupon
*
- * @param string $code Coupon Code
- * @param string $title Coupon title
- * @param float $amount Amount removed from the Total Checkout
- * @param string $effect Coupon effect
- * @param string $shortDescription Coupon short description
- * @param string $description Coupon description
- * @param boolean $isEnabled Enable/Disable
- * @param \DateTime $expirationDate Coupon expiration date
- * @param boolean $isAvailableOnSpecialOffers Is available on special offers
- * @param boolean $isCumulative Is cumulative
- * @param boolean $isRemovingPostage Is removing Postage
- * @param int $maxUsage Coupon quantity
- * @param CouponRuleCollection $rules CouponRuleInterface to add
- * @param string $locale Coupon Language code ISO (ex: fr_FR)
+ * @param string $code Coupon Code
+ * @param string $title Coupon title
+ * @param float $amount Amount removed from the Total Checkout
+ * @param string $effect Coupon effect
+ * @param bool $isRemovingPostage Is removing Postage
+ * @param string $shortDescription Coupon short description
+ * @param string $description Coupon description
+ * @param boolean $isEnabled Enable/Disable
+ * @param \DateTime $expirationDate Coupon expiration date
+ * @param boolean $isAvailableOnSpecialOffers Is available on special offers
+ * @param boolean $isCumulative Is cumulative
+ * @param int $maxUsage Coupon quantity
+ * @param string $locale Coupon Language code ISO (ex: fr_FR)
+ *
+ * @throws \Exception
*/
- function createOrUpdate($code, $title, $amount, $effect, $shortDescription, $description, $isEnabled, $expirationDate, $isAvailableOnSpecialOffers, $isCumulative, $maxUsage, $rules, $locale = null)
+ function createOrUpdate($code, $title, $amount, $effect, $isRemovingPostage, $shortDescription, $description, $isEnabled, $expirationDate, $isAvailableOnSpecialOffers, $isCumulative, $maxUsage, $locale = null)
{
$this->setCode($code)
->setTitle($title)
@@ -74,13 +75,13 @@ class Coupon extends BaseCoupon
->setDescription($description)
->setType($effect)
->setAmount($amount)
+ ->setIsRemovingPostage($isRemovingPostage)
->setType($amount)
->setIsEnabled($isEnabled)
->setExpirationDate($expirationDate)
->setIsAvailableOnSpecialOffers($isAvailableOnSpecialOffers)
->setIsCumulative($isCumulative)
- ->setMaxUsage($maxUsage)
- ->setRules($rules);
+ ->setMaxUsage($maxUsage);
// Set object language (i18n)
if (!is_null($locale)) {
@@ -99,33 +100,34 @@ class Coupon extends BaseCoupon
}
}
-// /**
-// * Set the value of [serialized_rules] column.
-// * Convert a CouponRuleCollection into a serialized array of SerializableRule
-// *
-// * @param CouponRuleCollection $rules A set of Rules
-// *
-// * @return \Thelia\Model\Coupon The current object (for fluent API support)
-// */
-// public function setRules(CouponRuleCollection $rules)
-// {
-// $serializedRules = null;
-// if ($rules !== null) {
-// /** @var $rule CouponRuleInterface */
-// foreach ($rules->getRules() as $rule) {
-// $serializedRules[] = $rule->getSerializableRule();
-// }
-//
-// $serializedRules = (string) base64_encode(serialize($serializedRules));
-// }
-//
-// if ($this->serialized_rules !== $serializedRules) {
-// $this->serialized_rules = $serializedRules;
-// $this->modifiedColumns[] = CouponTableMap::SERIALIZED_RULES;
-// }
-//
-// return $this;
-// }
+ /**
+ * Create or Update this coupon rule
+ *
+ * @param string $serializableRules Serialized rules ready to be saved
+ * @param string $locale Coupon Language code ISO (ex: fr_FR)
+ *
+ * @throws \Exception
+ */
+ function createOrUpdateRules($serializableRules, $locale)
+ {
+ $this->setSerializedRules($serializableRules);
+
+ // Set object language (i18n)
+ if (!is_null($locale)) {
+ $this->setLocale($locale);
+ }
+
+ $con = Propel::getWriteConnection(CouponTableMap::DATABASE_NAME);
+ $con->beginTransaction();
+ try {
+ $this->save($con);
+ $con->commit();
+
+ } catch(\Exception $e) {
+ $con->rollback();
+ throw $e;
+ }
+ }
diff --git a/core/lib/Thelia/Model/Map/TaxTableMap.php b/core/lib/Thelia/Model/Map/TaxTableMap.php
index 11e5047ce..6ca89ae85 100644
--- a/core/lib/Thelia/Model/Map/TaxTableMap.php
+++ b/core/lib/Thelia/Model/Map/TaxTableMap.php
@@ -57,7 +57,7 @@ class TaxTableMap extends TableMap
/**
* The total number of columns
*/
- const NUM_COLUMNS = 4;
+ const NUM_COLUMNS = 5;
/**
* The number of lazy-loaded columns
@@ -67,7 +67,7 @@ class TaxTableMap extends TableMap
/**
* The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS)
*/
- const NUM_HYDRATE_COLUMNS = 4;
+ const NUM_HYDRATE_COLUMNS = 5;
/**
* the column name for the ID field
@@ -75,9 +75,14 @@ class TaxTableMap extends TableMap
const ID = 'tax.ID';
/**
- * the column name for the RATE field
+ * the column name for the TYPE field
*/
- const RATE = 'tax.RATE';
+ const TYPE = 'tax.TYPE';
+
+ /**
+ * the column name for the SERIALIZED_REQUIREMENTS field
+ */
+ const SERIALIZED_REQUIREMENTS = 'tax.SERIALIZED_REQUIREMENTS';
/**
* the column name for the CREATED_AT field
@@ -110,12 +115,12 @@ class TaxTableMap extends TableMap
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
*/
protected static $fieldNames = array (
- self::TYPE_PHPNAME => array('Id', 'Rate', 'CreatedAt', 'UpdatedAt', ),
- self::TYPE_STUDLYPHPNAME => array('id', 'rate', 'createdAt', 'updatedAt', ),
- self::TYPE_COLNAME => array(TaxTableMap::ID, TaxTableMap::RATE, TaxTableMap::CREATED_AT, TaxTableMap::UPDATED_AT, ),
- self::TYPE_RAW_COLNAME => array('ID', 'RATE', 'CREATED_AT', 'UPDATED_AT', ),
- self::TYPE_FIELDNAME => array('id', 'rate', 'created_at', 'updated_at', ),
- self::TYPE_NUM => array(0, 1, 2, 3, )
+ self::TYPE_PHPNAME => array('Id', 'Type', 'SerializedRequirements', 'CreatedAt', 'UpdatedAt', ),
+ self::TYPE_STUDLYPHPNAME => array('id', 'type', 'serializedRequirements', 'createdAt', 'updatedAt', ),
+ self::TYPE_COLNAME => array(TaxTableMap::ID, TaxTableMap::TYPE, TaxTableMap::SERIALIZED_REQUIREMENTS, TaxTableMap::CREATED_AT, TaxTableMap::UPDATED_AT, ),
+ self::TYPE_RAW_COLNAME => array('ID', 'TYPE', 'SERIALIZED_REQUIREMENTS', 'CREATED_AT', 'UPDATED_AT', ),
+ self::TYPE_FIELDNAME => array('id', 'type', 'serialized_requirements', 'created_at', 'updated_at', ),
+ self::TYPE_NUM => array(0, 1, 2, 3, 4, )
);
/**
@@ -125,12 +130,12 @@ class TaxTableMap extends TableMap
* e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0
*/
protected static $fieldKeys = array (
- self::TYPE_PHPNAME => array('Id' => 0, 'Rate' => 1, 'CreatedAt' => 2, 'UpdatedAt' => 3, ),
- self::TYPE_STUDLYPHPNAME => array('id' => 0, 'rate' => 1, 'createdAt' => 2, 'updatedAt' => 3, ),
- self::TYPE_COLNAME => array(TaxTableMap::ID => 0, TaxTableMap::RATE => 1, TaxTableMap::CREATED_AT => 2, TaxTableMap::UPDATED_AT => 3, ),
- self::TYPE_RAW_COLNAME => array('ID' => 0, 'RATE' => 1, 'CREATED_AT' => 2, 'UPDATED_AT' => 3, ),
- self::TYPE_FIELDNAME => array('id' => 0, 'rate' => 1, 'created_at' => 2, 'updated_at' => 3, ),
- self::TYPE_NUM => array(0, 1, 2, 3, )
+ self::TYPE_PHPNAME => array('Id' => 0, 'Type' => 1, 'SerializedRequirements' => 2, 'CreatedAt' => 3, 'UpdatedAt' => 4, ),
+ self::TYPE_STUDLYPHPNAME => array('id' => 0, 'type' => 1, 'serializedRequirements' => 2, 'createdAt' => 3, 'updatedAt' => 4, ),
+ self::TYPE_COLNAME => array(TaxTableMap::ID => 0, TaxTableMap::TYPE => 1, TaxTableMap::SERIALIZED_REQUIREMENTS => 2, TaxTableMap::CREATED_AT => 3, TaxTableMap::UPDATED_AT => 4, ),
+ self::TYPE_RAW_COLNAME => array('ID' => 0, 'TYPE' => 1, 'SERIALIZED_REQUIREMENTS' => 2, 'CREATED_AT' => 3, 'UPDATED_AT' => 4, ),
+ self::TYPE_FIELDNAME => array('id' => 0, 'type' => 1, 'serialized_requirements' => 2, 'created_at' => 3, 'updated_at' => 4, ),
+ self::TYPE_NUM => array(0, 1, 2, 3, 4, )
);
/**
@@ -150,7 +155,8 @@ class TaxTableMap extends TableMap
$this->setUseIdGenerator(true);
// columns
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
- $this->addColumn('RATE', 'Rate', 'FLOAT', true, null, null);
+ $this->addColumn('TYPE', 'Type', 'VARCHAR', true, 255, null);
+ $this->addColumn('SERIALIZED_REQUIREMENTS', 'SerializedRequirements', 'LONGVARCHAR', true, null, null);
$this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null);
$this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null);
} // initialize()
@@ -327,12 +333,14 @@ class TaxTableMap extends TableMap
{
if (null === $alias) {
$criteria->addSelectColumn(TaxTableMap::ID);
- $criteria->addSelectColumn(TaxTableMap::RATE);
+ $criteria->addSelectColumn(TaxTableMap::TYPE);
+ $criteria->addSelectColumn(TaxTableMap::SERIALIZED_REQUIREMENTS);
$criteria->addSelectColumn(TaxTableMap::CREATED_AT);
$criteria->addSelectColumn(TaxTableMap::UPDATED_AT);
} else {
$criteria->addSelectColumn($alias . '.ID');
- $criteria->addSelectColumn($alias . '.RATE');
+ $criteria->addSelectColumn($alias . '.TYPE');
+ $criteria->addSelectColumn($alias . '.SERIALIZED_REQUIREMENTS');
$criteria->addSelectColumn($alias . '.CREATED_AT');
$criteria->addSelectColumn($alias . '.UPDATED_AT');
}
diff --git a/core/lib/Thelia/Model/Tax.php b/core/lib/Thelia/Model/Tax.php
index 21efbae6a..738f16508 100755
--- a/core/lib/Thelia/Model/Tax.php
+++ b/core/lib/Thelia/Model/Tax.php
@@ -4,6 +4,7 @@ namespace Thelia\Model;
use Thelia\Exception\TaxEngineException;
use Thelia\Model\Base\Tax as BaseTax;
+use Thelia\TaxEngine\TaxType\BaseTaxType;
class Tax extends BaseTax
{
@@ -33,14 +34,37 @@ class Tax extends BaseTax
return $taxRuleCountryPosition;
}
- public function getTaxRuleRateSum()
+ public function getTypeInstance()
{
- try {
- $taxRuleRateSum = $this->getVirtualColumn(TaxRuleQuery::ALIAS_FOR_TAX_RATE_SUM);
- } catch(PropelException $e) {
- throw new PropelException("Virtual column `" . TaxRuleQuery::ALIAS_FOR_TAX_RATE_SUM . "` does not exist in Tax::getTaxRuleRateSum");
+ $class = '\\Thelia\\TaxEngine\\TaxType\\' . $this->getType();
+
+ /* test type */
+ if(!class_exists($class)) {
+ throw new TaxEngineException('Recorded type does not exists', TaxEngineException::BAD_RECORDED_TYPE);
}
- return $taxRuleRateSum;
+ $instance = new $class;
+
+ if(!$instance instanceof BaseTaxType) {
+ throw new TaxEngineException('Recorded type does not extends BaseTaxType', TaxEngineException::BAD_RECORDED_TYPE);
+ }
+
+ return $instance;
+ }
+
+ public function setRequirements($requirements)
+ {
+ parent::setSerializedRequirements(base64_encode(json_encode($requirements)));
+ }
+
+ public function getRequirements()
+ {
+ $requirements = json_decode(base64_decode(parent::getSerializedRequirements()), true);
+
+ if(json_last_error() != JSON_ERROR_NONE || !is_array($requirements)) {
+ throw new TaxEngineException('BAD RECORDED REQUIREMENTS', TaxEngineException::BAD_RECORDED_REQUIREMENTS);
+ }
+
+ return $requirements;
}
}
diff --git a/core/lib/Thelia/Model/TaxRuleQuery.php b/core/lib/Thelia/Model/TaxRuleQuery.php
index f9c6cf1e5..d5ce47546 100755
--- a/core/lib/Thelia/Model/TaxRuleQuery.php
+++ b/core/lib/Thelia/Model/TaxRuleQuery.php
@@ -20,21 +20,18 @@ use Thelia\Model\Map\TaxTableMap;
class TaxRuleQuery extends BaseTaxRuleQuery
{
const ALIAS_FOR_TAX_RULE_COUNTRY_POSITION = 'taxRuleCountryPosition';
- const ALIAS_FOR_TAX_RATE_SUM = 'taxRateSum';
- public function getTaxCalculatorGroupedCollection(Product $product, Country $country)
+ public function getTaxCalculatorCollection(Product $product, Country $country)
{
$search = TaxQuery::create()
->filterByTaxRuleCountry(
TaxRuleCountryQuery::create()
->filterByCountry($country, Criteria::EQUAL)
->filterByTaxRuleId($product->getTaxRuleId())
- ->groupByPosition()
->orderByPosition()
->find()
)
->withColumn(TaxRuleCountryTableMap::POSITION, self::ALIAS_FOR_TAX_RULE_COUNTRY_POSITION)
- ->withColumn('ROUND(SUM(' . TaxTableMap::RATE . '), 2)', self::ALIAS_FOR_TAX_RATE_SUM)
;
return $search->find();
diff --git a/core/lib/Thelia/TaxEngine/Calculator.php b/core/lib/Thelia/TaxEngine/Calculator.php
index 66c4fcbbf..2708e88c6 100755
--- a/core/lib/Thelia/TaxEngine/Calculator.php
+++ b/core/lib/Thelia/TaxEngine/Calculator.php
@@ -34,9 +34,12 @@ use Thelia\Model\TaxRuleQuery;
*/
class Calculator
{
+ /**
+ * @var TaxRuleQuery
+ */
protected $taxRuleQuery = null;
- protected $taxRulesGroupedCollection = null;
+ protected $taxRulesCollection = null;
protected $product = null;
protected $country = null;
@@ -50,7 +53,7 @@ class Calculator
{
$this->product = null;
$this->country = null;
- $this->taxRulesGroupedCollection = null;
+ $this->taxRulesCollection = null;
if($product->getId() === null) {
throw new TaxEngineException('Product id is empty in Calculator::load', TaxEngineException::UNDEFINED_PRODUCT);
@@ -62,34 +65,38 @@ class Calculator
$this->product = $product;
$this->country = $country;
- $this->taxRulesGroupedCollection = $this->taxRuleQuery->getTaxCalculatorGroupedCollection($product, $country);
+ $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($product, $country);
return $this;
}
- public function getTaxAmount($amount)
+ public function getTaxAmount($untaxedPrice)
{
- if(null === $this->taxRulesGroupedCollection) {
+ if(null === $this->taxRulesCollection) {
throw new TaxEngineException('Tax rules collection is empty in Calculator::getTaxAmount', TaxEngineException::UNDEFINED_TAX_RULES_COLLECTION);
}
- if(false === filter_var($amount, FILTER_VALIDATE_FLOAT)) {
+ if(false === filter_var($untaxedPrice, FILTER_VALIDATE_FLOAT)) {
throw new TaxEngineException('BAD AMOUNT FORMAT', TaxEngineException::BAD_AMOUNT_FORMAT);
}
$totalTaxAmount = 0;
- foreach($this->taxRulesGroupedCollection as $taxRule) {
- $rateSum = $taxRule->getTaxRuleRateSum();
- $taxAmount = $amount * $rateSum * 0.01;
+ foreach($this->taxRulesCollection as $taxRule) {
+ $taxType = $taxRule->getTypeInstance();
+
+ $taxType->loadRequirements($taxRule->getRequirements());
+
+ $taxAmount = $taxType->calculate($untaxedPrice);
+
$totalTaxAmount += $taxAmount;
- $amount += $taxAmount;
+ $untaxedPrice += $taxAmount;
}
return $totalTaxAmount;
}
- public function getTaxedPrice($amount)
+ public function getTaxedPrice($untaxedPrice)
{
- return $amount + $this->getTaxAmount($amount);
+ return $untaxedPrice + $this->getTaxAmount($untaxedPrice);
}
}
diff --git a/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php
new file mode 100755
index 000000000..7f487bf64
--- /dev/null
+++ b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php
@@ -0,0 +1,78 @@
+. */
+/* */
+/*************************************************************************************/
+namespace Thelia\TaxEngine\TaxType;
+
+use Thelia\Exception\TaxEngineException;
+use Thelia\Type\TypeInterface;
+
+/**
+ *
+ * @author Etienne Roudeix | {intl l='Conditions'} | -{intl l='Actions'} | -
|---|---|
| *} - {*{if !$smarty.foreach.rulesForeach.first}*} - {*{intl l='And'}*} - {*{/if}*} - {*{$rule.tooltip nofilter}*} - {* | *} - - {**} - {* {intl l='Edit'}*} - {* {intl l='Delete'}*} - {* | *} - {*
| {intl l='Conditions'} | +{intl l='Actions'} | +
|---|
| *} + {* |
| Categories list | *} + {*
|---|
| *} + {* |
| *} - {* |
| Categories list | *} - {*
|---|
| *} - {* |