From e8a4e56324fc930455f07bf9bfe0fa0dbbdbb715 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 11 Sep 2013 16:21:51 +0200 Subject: [PATCH] tax engine --- .../Thelia/Exception/TaxEngineException.php | 3 + core/lib/Thelia/Exception/TypeException.php | 39 ++++++ core/lib/Thelia/Model/Base/Tax.php | 132 +++++++++++++----- core/lib/Thelia/Model/Base/TaxQuery.php | 79 +++++++---- core/lib/Thelia/Model/Map/TaxTableMap.php | 46 +++--- core/lib/Thelia/Model/Tax.php | 37 ++++- core/lib/Thelia/Model/TaxRuleQuery.php | 3 - core/lib/Thelia/TaxEngine/Calculator.php | 18 ++- .../Thelia/TaxEngine/TaxType/BaseTaxType.php | 83 +++++++++++ .../TaxType/FeatureSlicePercentTaxType.php | 47 +++++++ .../TaxEngine/TaxType/PricePercentTaxType.php | 45 ++++++ .../lib/Thelia/Type/FloatToFloatArrayType.php | 56 ++++++++ core/lib/Thelia/Type/ModelType.php | 66 +++++++++ install/insert.sql | 6 +- install/thelia.sql | 3 +- local/config/schema.xml | 3 +- templates/default_save/product.html | 2 +- 17 files changed, 561 insertions(+), 107 deletions(-) create mode 100755 core/lib/Thelia/Exception/TypeException.php create mode 100755 core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php create mode 100755 core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php create mode 100755 core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php create mode 100755 core/lib/Thelia/Type/FloatToFloatArrayType.php create mode 100755 core/lib/Thelia/Type/ModelType.php diff --git a/core/lib/Thelia/Exception/TaxEngineException.php b/core/lib/Thelia/Exception/TaxEngineException.php index d56862ab6..8ce8561ef 100755 --- a/core/lib/Thelia/Exception/TaxEngineException.php +++ b/core/lib/Thelia/Exception/TaxEngineException.php @@ -27,6 +27,9 @@ class TaxEngineException extends \RuntimeException { const UNKNOWN_EXCEPTION = 0; + const BAD_RECORDED_TYPE = 101; + const BAD_RECORDED_REQUIREMENTS = 102; + const UNDEFINED_PRODUCT = 501; const UNDEFINED_COUNTRY = 502; const UNDEFINED_TAX_RULES_COLLECTION = 503; diff --git a/core/lib/Thelia/Exception/TypeException.php b/core/lib/Thelia/Exception/TypeException.php new file mode 100755 index 000000000..442fffe3e --- /dev/null +++ b/core/lib/Thelia/Exception/TypeException.php @@ -0,0 +1,39 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Exception; + +class TypeException extends \RuntimeException +{ + const UNKNOWN_EXCEPTION = 0; + + const MODEL_NOT_FOUND = 404; + + public function __construct($message, $code = null, $previous = null) + { + if ($code === null) { + $code = self::UNKNOWN_EXCEPTION; + } + parent::__construct($message, $code, $previous); + } +} diff --git a/core/lib/Thelia/Model/Base/Tax.php b/core/lib/Thelia/Model/Base/Tax.php index ed4bb40f7..da674b5cf 100644 --- a/core/lib/Thelia/Model/Base/Tax.php +++ b/core/lib/Thelia/Model/Base/Tax.php @@ -66,10 +66,16 @@ abstract class Tax implements ActiveRecordInterface protected $id; /** - * The value for the rate field. - * @var double + * The value for the type field. + * @var string */ - protected $rate; + protected $type; + + /** + * The value for the serialized_requirements field. + * @var string + */ + protected $serialized_requirements; /** * The value for the created_at field. @@ -395,14 +401,25 @@ abstract class Tax implements ActiveRecordInterface } /** - * Get the [rate] column value. + * Get the [type] column value. * - * @return double + * @return string */ - public function getRate() + public function getType() { - return $this->rate; + return $this->type; + } + + /** + * Get the [serialized_requirements] column value. + * + * @return string + */ + public function getSerializedRequirements() + { + + return $this->serialized_requirements; } /** @@ -467,25 +484,46 @@ abstract class Tax implements ActiveRecordInterface } // setId() /** - * Set the value of [rate] column. + * Set the value of [type] column. * - * @param double $v new value + * @param string $v new value * @return \Thelia\Model\Tax The current object (for fluent API support) */ - public function setRate($v) + public function setType($v) { if ($v !== null) { - $v = (double) $v; + $v = (string) $v; } - if ($this->rate !== $v) { - $this->rate = $v; - $this->modifiedColumns[] = TaxTableMap::RATE; + if ($this->type !== $v) { + $this->type = $v; + $this->modifiedColumns[] = TaxTableMap::TYPE; } return $this; - } // setRate() + } // setType() + + /** + * Set the value of [serialized_requirements] column. + * + * @param string $v new value + * @return \Thelia\Model\Tax The current object (for fluent API support) + */ + public function setSerializedRequirements($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->serialized_requirements !== $v) { + $this->serialized_requirements = $v; + $this->modifiedColumns[] = TaxTableMap::SERIALIZED_REQUIREMENTS; + } + + + return $this; + } // setSerializedRequirements() /** * Sets the value of [created_at] column to a normalized version of the date/time value specified. @@ -569,16 +607,19 @@ abstract class Tax implements ActiveRecordInterface $col = $row[TableMap::TYPE_NUM == $indexType ? 0 + $startcol : TaxTableMap::translateFieldName('Id', TableMap::TYPE_PHPNAME, $indexType)]; $this->id = (null !== $col) ? (int) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 1 + $startcol : TaxTableMap::translateFieldName('Rate', TableMap::TYPE_PHPNAME, $indexType)]; - $this->rate = (null !== $col) ? (double) $col : null; + $col = $row[TableMap::TYPE_NUM == $indexType ? 1 + $startcol : TaxTableMap::translateFieldName('Type', TableMap::TYPE_PHPNAME, $indexType)]; + $this->type = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 2 + $startcol : TaxTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 2 + $startcol : TaxTableMap::translateFieldName('SerializedRequirements', TableMap::TYPE_PHPNAME, $indexType)]; + $this->serialized_requirements = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 3 + $startcol : TaxTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } $this->created_at = (null !== $col) ? PropelDateTime::newInstance($col, null, '\DateTime') : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 3 + $startcol : TaxTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 4 + $startcol : TaxTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } @@ -591,7 +632,7 @@ abstract class Tax implements ActiveRecordInterface $this->ensureConsistency(); } - return $startcol + 4; // 4 = TaxTableMap::NUM_HYDRATE_COLUMNS. + return $startcol + 5; // 5 = TaxTableMap::NUM_HYDRATE_COLUMNS. } catch (Exception $e) { throw new PropelException("Error populating \Thelia\Model\Tax object", 0, $e); @@ -852,8 +893,11 @@ abstract class Tax implements ActiveRecordInterface if ($this->isColumnModified(TaxTableMap::ID)) { $modifiedColumns[':p' . $index++] = 'ID'; } - if ($this->isColumnModified(TaxTableMap::RATE)) { - $modifiedColumns[':p' . $index++] = 'RATE'; + if ($this->isColumnModified(TaxTableMap::TYPE)) { + $modifiedColumns[':p' . $index++] = 'TYPE'; + } + if ($this->isColumnModified(TaxTableMap::SERIALIZED_REQUIREMENTS)) { + $modifiedColumns[':p' . $index++] = 'SERIALIZED_REQUIREMENTS'; } if ($this->isColumnModified(TaxTableMap::CREATED_AT)) { $modifiedColumns[':p' . $index++] = 'CREATED_AT'; @@ -875,8 +919,11 @@ abstract class Tax implements ActiveRecordInterface case 'ID': $stmt->bindValue($identifier, $this->id, PDO::PARAM_INT); break; - case 'RATE': - $stmt->bindValue($identifier, $this->rate, PDO::PARAM_STR); + case 'TYPE': + $stmt->bindValue($identifier, $this->type, PDO::PARAM_STR); + break; + case 'SERIALIZED_REQUIREMENTS': + $stmt->bindValue($identifier, $this->serialized_requirements, PDO::PARAM_STR); break; case 'CREATED_AT': $stmt->bindValue($identifier, $this->created_at ? $this->created_at->format("Y-m-d H:i:s") : null, PDO::PARAM_STR); @@ -950,12 +997,15 @@ abstract class Tax implements ActiveRecordInterface return $this->getId(); break; case 1: - return $this->getRate(); + return $this->getType(); break; case 2: - return $this->getCreatedAt(); + return $this->getSerializedRequirements(); break; case 3: + return $this->getCreatedAt(); + break; + case 4: return $this->getUpdatedAt(); break; default: @@ -988,9 +1038,10 @@ abstract class Tax implements ActiveRecordInterface $keys = TaxTableMap::getFieldNames($keyType); $result = array( $keys[0] => $this->getId(), - $keys[1] => $this->getRate(), - $keys[2] => $this->getCreatedAt(), - $keys[3] => $this->getUpdatedAt(), + $keys[1] => $this->getType(), + $keys[2] => $this->getSerializedRequirements(), + $keys[3] => $this->getCreatedAt(), + $keys[4] => $this->getUpdatedAt(), ); $virtualColumns = $this->virtualColumns; foreach($virtualColumns as $key => $virtualColumn) @@ -1043,12 +1094,15 @@ abstract class Tax implements ActiveRecordInterface $this->setId($value); break; case 1: - $this->setRate($value); + $this->setType($value); break; case 2: - $this->setCreatedAt($value); + $this->setSerializedRequirements($value); break; case 3: + $this->setCreatedAt($value); + break; + case 4: $this->setUpdatedAt($value); break; } // switch() @@ -1076,9 +1130,10 @@ abstract class Tax implements ActiveRecordInterface $keys = TaxTableMap::getFieldNames($keyType); if (array_key_exists($keys[0], $arr)) $this->setId($arr[$keys[0]]); - if (array_key_exists($keys[1], $arr)) $this->setRate($arr[$keys[1]]); - if (array_key_exists($keys[2], $arr)) $this->setCreatedAt($arr[$keys[2]]); - if (array_key_exists($keys[3], $arr)) $this->setUpdatedAt($arr[$keys[3]]); + if (array_key_exists($keys[1], $arr)) $this->setType($arr[$keys[1]]); + if (array_key_exists($keys[2], $arr)) $this->setSerializedRequirements($arr[$keys[2]]); + if (array_key_exists($keys[3], $arr)) $this->setCreatedAt($arr[$keys[3]]); + if (array_key_exists($keys[4], $arr)) $this->setUpdatedAt($arr[$keys[4]]); } /** @@ -1091,7 +1146,8 @@ abstract class Tax implements ActiveRecordInterface $criteria = new Criteria(TaxTableMap::DATABASE_NAME); if ($this->isColumnModified(TaxTableMap::ID)) $criteria->add(TaxTableMap::ID, $this->id); - if ($this->isColumnModified(TaxTableMap::RATE)) $criteria->add(TaxTableMap::RATE, $this->rate); + if ($this->isColumnModified(TaxTableMap::TYPE)) $criteria->add(TaxTableMap::TYPE, $this->type); + if ($this->isColumnModified(TaxTableMap::SERIALIZED_REQUIREMENTS)) $criteria->add(TaxTableMap::SERIALIZED_REQUIREMENTS, $this->serialized_requirements); if ($this->isColumnModified(TaxTableMap::CREATED_AT)) $criteria->add(TaxTableMap::CREATED_AT, $this->created_at); if ($this->isColumnModified(TaxTableMap::UPDATED_AT)) $criteria->add(TaxTableMap::UPDATED_AT, $this->updated_at); @@ -1157,7 +1213,8 @@ abstract class Tax implements ActiveRecordInterface */ public function copyInto($copyObj, $deepCopy = false, $makeNew = true) { - $copyObj->setRate($this->getRate()); + $copyObj->setType($this->getType()); + $copyObj->setSerializedRequirements($this->getSerializedRequirements()); $copyObj->setCreatedAt($this->getCreatedAt()); $copyObj->setUpdatedAt($this->getUpdatedAt()); @@ -1729,7 +1786,8 @@ abstract class Tax implements ActiveRecordInterface public function clear() { $this->id = null; - $this->rate = null; + $this->type = null; + $this->serialized_requirements = null; $this->created_at = null; $this->updated_at = null; $this->alreadyInSave = false; diff --git a/core/lib/Thelia/Model/Base/TaxQuery.php b/core/lib/Thelia/Model/Base/TaxQuery.php index 07316bf69..93da10f53 100644 --- a/core/lib/Thelia/Model/Base/TaxQuery.php +++ b/core/lib/Thelia/Model/Base/TaxQuery.php @@ -23,12 +23,14 @@ use Thelia\Model\Map\TaxTableMap; * * * @method ChildTaxQuery orderById($order = Criteria::ASC) Order by the id column - * @method ChildTaxQuery orderByRate($order = Criteria::ASC) Order by the rate column + * @method ChildTaxQuery orderByType($order = Criteria::ASC) Order by the type column + * @method ChildTaxQuery orderBySerializedRequirements($order = Criteria::ASC) Order by the serialized_requirements column * @method ChildTaxQuery orderByCreatedAt($order = Criteria::ASC) Order by the created_at column * @method ChildTaxQuery orderByUpdatedAt($order = Criteria::ASC) Order by the updated_at column * * @method ChildTaxQuery groupById() Group by the id column - * @method ChildTaxQuery groupByRate() Group by the rate column + * @method ChildTaxQuery groupByType() Group by the type column + * @method ChildTaxQuery groupBySerializedRequirements() Group by the serialized_requirements column * @method ChildTaxQuery groupByCreatedAt() Group by the created_at column * @method ChildTaxQuery groupByUpdatedAt() Group by the updated_at column * @@ -48,12 +50,14 @@ use Thelia\Model\Map\TaxTableMap; * @method ChildTax findOneOrCreate(ConnectionInterface $con = null) Return the first ChildTax matching the query, or a new ChildTax object populated from the query conditions when no match is found * * @method ChildTax findOneById(int $id) Return the first ChildTax filtered by the id column - * @method ChildTax findOneByRate(double $rate) Return the first ChildTax filtered by the rate column + * @method ChildTax findOneByType(string $type) Return the first ChildTax filtered by the type column + * @method ChildTax findOneBySerializedRequirements(string $serialized_requirements) Return the first ChildTax filtered by the serialized_requirements column * @method ChildTax findOneByCreatedAt(string $created_at) Return the first ChildTax filtered by the created_at column * @method ChildTax findOneByUpdatedAt(string $updated_at) Return the first ChildTax filtered by the updated_at column * * @method array findById(int $id) Return ChildTax objects filtered by the id column - * @method array findByRate(double $rate) Return ChildTax objects filtered by the rate column + * @method array findByType(string $type) Return ChildTax objects filtered by the type column + * @method array findBySerializedRequirements(string $serialized_requirements) Return ChildTax objects filtered by the serialized_requirements column * @method array findByCreatedAt(string $created_at) Return ChildTax objects filtered by the created_at column * @method array findByUpdatedAt(string $updated_at) Return ChildTax objects filtered by the updated_at column * @@ -144,7 +148,7 @@ abstract class TaxQuery extends ModelCriteria */ protected function findPkSimple($key, $con) { - $sql = 'SELECT ID, RATE, CREATED_AT, UPDATED_AT FROM tax WHERE ID = :p0'; + $sql = 'SELECT ID, TYPE, SERIALIZED_REQUIREMENTS, CREATED_AT, UPDATED_AT FROM tax WHERE ID = :p0'; try { $stmt = $con->prepare($sql); $stmt->bindValue(':p0', $key, PDO::PARAM_INT); @@ -275,44 +279,61 @@ abstract class TaxQuery extends ModelCriteria } /** - * Filter the query on the rate column + * Filter the query on the type column * * Example usage: * - * $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/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..58144d551 100755 --- a/core/lib/Thelia/Model/Tax.php +++ b/core/lib/Thelia/Model/Tax.php @@ -2,8 +2,10 @@ namespace Thelia\Model; +use Symfony\Component\Form\Exception\Exception; use Thelia\Exception\TaxEngineException; use Thelia\Model\Base\Tax as BaseTax; +use Thelia\TaxEngine\TaxType\BaseTaxType; class Tax extends BaseTax { @@ -33,14 +35,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..75a21a453 100755 --- a/core/lib/Thelia/Model/TaxRuleQuery.php +++ b/core/lib/Thelia/Model/TaxRuleQuery.php @@ -20,7 +20,6 @@ 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) { @@ -29,12 +28,10 @@ class TaxRuleQuery extends BaseTaxRuleQuery 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 e077a24b8..1630ad550 100755 --- a/core/lib/Thelia/TaxEngine/Calculator.php +++ b/core/lib/Thelia/TaxEngine/Calculator.php @@ -36,7 +36,7 @@ class Calculator { protected $taxRuleQuery = null; - protected $taxRulesGroupedCollection = null; + protected $taxRulesCollection = null; protected $product = null; protected $country = null; @@ -50,7 +50,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,14 +62,14 @@ class Calculator $this->product = $product; $this->country = $country; - $this->taxRulesGroupedCollection = $this->taxRuleQuery->getTaxCalculatorGroupedCollection($product, $country); + $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorGroupedCollection($product, $country); return $this; } 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); } @@ -78,9 +78,13 @@ class Calculator } $totalTaxAmount = 0; - foreach($this->taxRulesGroupedCollection as $taxRule) { - $rateSum = $taxRule->getTaxRuleRateSum(); - $taxAmount = $untaxedPrice * $rateSum * 0.01; + foreach($this->taxRulesCollection as $taxRule) { + $taxType = $taxRule->getTypeInstance(); + + $taxType->loadRequirements($taxRule->getRequirements()); + + $taxAmount = $taxType->calculate($untaxedPrice); + $totalTaxAmount += $taxAmount; $untaxedPrice += $taxAmount; } diff --git a/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php new file mode 100755 index 000000000..d155bb4c0 --- /dev/null +++ b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php @@ -0,0 +1,83 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\TaxEngine\TaxType; + +use Thelia\Type\TypeInterface; + +/** + * + * @author Etienne Roudeix + * + */ +abstract class BaseTaxType +{ + protected $requirements = null; + + public abstract function calculate($untaxedPrice); + + public abstract function getRequirementsList(); + + public function loadRequirements($requirementsValues) + { + $this->requirements = $this->getRequirementsList(); + + if(!is_array($this->requirements)) { + //@todo throw sg + exit('err_1'); + } + + foreach($this->requirements as $requirement => $requirementType) { + if(!$requirementType instanceof TypeInterface) { + //@todo throw sg + exit('err_2'); + } + + if(!array_key_exists($requirement, $requirementsValues)) { + //@todo throw sg + exit('err_3'); + } + + if(!$requirementType->isValid($requirementsValues[$requirement])) { + //@todo throw sg + exit('err_4'); + } + + $this->requirements[$requirement] = $requirementsValues[$requirement]; + } + } + + public function getRequirement($key) + { + if($this->requirements === null) { + //@todo throw sg + exit('err_5'); + } + + if(!array_key_exists($key, $this->requirements)) { + //@todo throw sg + exit('err_6'); + } + + return $this->requirements[$key]; + } +} diff --git a/core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php new file mode 100755 index 000000000..4485f1e21 --- /dev/null +++ b/core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php @@ -0,0 +1,47 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\TaxEngine\TaxType; + +use Thelia\Type\FloatToFloatArrayType; +use Thelia\Type\ModelType; + +/** + * + * @author Etienne Roudeix + * + */ +class featureSlicePercentTaxType extends BaseTaxType +{ + public function calculate($untaxedPrice) + { + + } + + public function getRequirementsList() + { + return array( + 'featureId' => new ModelType('Currency'), + 'slices' => new FloatToFloatArrayType(), + ); + } +} diff --git a/core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php new file mode 100755 index 000000000..1d7152fcf --- /dev/null +++ b/core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php @@ -0,0 +1,45 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\TaxEngine\TaxType; + +use Thelia\Type\FloatType; + +/** + * + * @author Etienne Roudeix + * + */ +class PricePercentTaxType extends BaseTaxType +{ + public function calculate($untaxedPrice) + { + return $untaxedPrice * $this->getRequirement("percent") * 0.01; + } + + public function getRequirementsList() + { + return array( + 'percent' => new FloatType(), + ); + } +} diff --git a/core/lib/Thelia/Type/FloatToFloatArrayType.php b/core/lib/Thelia/Type/FloatToFloatArrayType.php new file mode 100755 index 000000000..5a70dba91 --- /dev/null +++ b/core/lib/Thelia/Type/FloatToFloatArrayType.php @@ -0,0 +1,56 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Type; + +/** + * + * @author Etienne Roudeix + * + */ + +class FloatToFloatArrayType implements TypeInterface +{ + public function getType() + { + return 'Float key to float value array type'; + } + + public function isValid($value) + { + if(!is_array($value)) + return false; + + foreach($value as $key => $value) { + if( filter_var($key, FILTER_VALIDATE_FLOAT) === false || filter_var($value, FILTER_VALIDATE_FLOAT) === false ) { + return false; + } + } + + return true; + } + + public function getFormattedValue($value) + { + return $this->isValid($value) ? $value : null; + } +} diff --git a/core/lib/Thelia/Type/ModelType.php b/core/lib/Thelia/Type/ModelType.php new file mode 100755 index 000000000..38df857e4 --- /dev/null +++ b/core/lib/Thelia/Type/ModelType.php @@ -0,0 +1,66 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Type; + +use Propel\Runtime\ActiveRecord\ActiveRecordInterface; +use Thelia\Exception\TypeException; + +/** + * + * @author Etienne Roudeix + * + */ +class ModelType implements TypeInterface +{ + protected $expectedModelActiveRecord = null; + + /** + * @param $expectedModelActiveRecord + * @throws TypeException + */ + public function __construct($expectedModelActiveRecord) + { + $class = '\\Thelia\\Model\\' . $expectedModelActiveRecord; + + if(!(class_exists($class) && new $class instanceof ActiveRecordInterface)) { + throw new TypeException('MODEL NOT FOUND', TypeException::MODEL_NOT_FOUND); + } + + $this->expectedModelActiveRecord = $class; + } + + public function getType() + { + return 'Model type'; + } + + public function isValid($value) + { + return $value instanceof $this->expectedModelActiveRecord; + } + + public function getFormattedValue($value) + { + return $this->isValid($value) ? $value : null; + } +} diff --git a/install/insert.sql b/install/insert.sql index da1414ce3..8ffdcb092 100755 --- a/install/insert.sql +++ b/install/insert.sql @@ -17,7 +17,7 @@ INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updat ('image_cache_dir_from_web_root', 'cache/images', 0, 0, NOW(), NOW()), ('currency_rate_update_url', 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml', 0, 0, NOW(), NOW()), ('page_not_found_view', '404.html', 0, 0, NOW(), NOW()), -('use_tax_free_amounts', 1, 1, 0, NOW(), NOW()); +('use_tax_free_amounts', 0, 1, 0, NOW(), NOW()); INSERT INTO `module` (`id`, `code`, `type`, `activate`, `position`, `full_namespace`, `created_at`, `updated_at`) VALUES @@ -1112,9 +1112,9 @@ INSERT INTO `country_i18n` (`id`, `locale`, `title`, `description`, `chapo`, `po (268, 'es_ES', 'USA - Alabama', '', '', ''), (268, 'fr_FR', 'USA - Alabama', '', '', ''); -INSERT INTO `tax` (`id`, `rate`, `created_at`, `updated_at`) +INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `created_at`, `updated_at`) VALUES - (1, '19.6', NOW(), NOW()); + (1, 'PricePercentTaxType', 'eyJwZXJjZW50IjoxOS42fQ==', NOW(), NOW()); INSERT INTO `tax_i18n` (`id`, `locale`, `title`) VALUES diff --git a/install/thelia.sql b/install/thelia.sql index 0346ffba8..e4e5fb6b7 100755 --- a/install/thelia.sql +++ b/install/thelia.sql @@ -111,7 +111,8 @@ DROP TABLE IF EXISTS `tax`; CREATE TABLE `tax` ( `id` INTEGER NOT NULL AUTO_INCREMENT, - `rate` FLOAT NOT NULL, + `type` VARCHAR(255) NOT NULL, + `serialized_requirements` TEXT NOT NULL, `created_at` DATETIME, `updated_at` DATETIME, PRIMARY KEY (`id`) diff --git a/local/config/schema.xml b/local/config/schema.xml index afb7b2c41..7590da1e4 100755 --- a/local/config/schema.xml +++ b/local/config/schema.xml @@ -86,7 +86,8 @@ - + + diff --git a/templates/default_save/product.html b/templates/default_save/product.html index 441585aba..cde8b61eb 100755 --- a/templates/default_save/product.html +++ b/templates/default_save/product.html @@ -8,7 +8,7 @@ Index : {navigate to="index"}
{ifloop rel="product"} -{loop type="product" name="product" current="true" min_price="50" max_price="100"} +{loop type="product" name="product" current="true"}

PRODUCT ({$ID}) : {$REF}