order process

This commit is contained in:
Etienne Roudeix
2013-09-20 16:25:46 +02:00
parent 17548e3526
commit 8541499302
24 changed files with 718 additions and 95 deletions

View File

@@ -24,20 +24,14 @@
namespace Thelia\Action;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Exception\OrderException;
use Thelia\Model\AttributeAvI18n;
use Thelia\Model\AttributeAvI18nQuery;
use Thelia\Model\AttributeI18n;
use Thelia\Model\AttributeI18nQuery;
use Thelia\Exception\TheliaProcessException;
use Thelia\Model\AddressQuery;
use Thelia\Model\OrderProductAttributeCombination;
use Thelia\Model\ProductI18nQuery;
use Thelia\Model\Lang;
use Thelia\Model\ModuleQuery;
use Thelia\Model\OrderProduct;
use Thelia\Model\OrderStatus;
@@ -45,7 +39,7 @@ use Thelia\Model\Map\OrderTableMap;
use Thelia\Model\OrderAddress;
use Thelia\Model\OrderStatusQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Model\ProductI18n;
use Thelia\Tools\I18n;
/**
*
@@ -125,6 +119,7 @@ class Order extends BaseAction implements EventSubscriberInterface
$currency = $this->getSession()->getCurrency();
$lang = $this->getSession()->getLang();
$deliveryAddress = AddressQuery::create()->findPk($sessionOrder->chosenDeliveryAddress);
$taxCountry = $deliveryAddress->getCountry();
$invoiceAddress = AddressQuery::create()->findPk($sessionOrder->chosenInvoiceAddress);
$cart = $this->getSession()->getCart();
$cartItems = $cart->getCartItems();
@@ -182,23 +177,30 @@ class Order extends BaseAction implements EventSubscriberInterface
foreach($cartItems as $cartItem) {
$product = $cartItem->getProduct();
/* get customer translation */
$productI18n = $this->getI18n(ProductI18nQuery::create(), new ProductI18n(), $product->getId());
/* get translation */
$productI18n = I18n::forceI18nRetrieving($this->getSession()->getLang()->getLocale(), 'Product', $product->getId());
$pse = $cartItem->getProductSaleElements();
/* check still in stock */
if($cartItem->getQuantity() > $pse->getQuantity()) {
$e = new OrderException("Not enough stock", OrderException::NOT_ENOUGH_STOCK);
$e->cartItem = $cartItem;
throw $e;
throw new TheliaProcessException("Not enough stock", TheliaProcessException::CART_ITEM_NOT_ENOUGH_STOCK, $cartItem);
}
/* decrease stock */
$pse->setQuantity(
$pse->getQuantity() - $cartItem->getQuantity()
);
$pse->save();
$pse->save($con);
/* get tax */
$taxRuleI18n = I18n::forceI18nRetrieving($this->getSession()->getLang()->getLocale(), 'TaxRule', $product->getTaxRuleId());
$taxDetail = $product->getTaxRule()->getTaxDetail(
$taxCountry,
$cartItem->getPromo() == 1 ? $cartItem->getPromoPrice() : $cartItem->getPrice(),
$this->getSession()->getLang()->getLocale()
);
$orderProduct = new OrderProduct();
$orderProduct
@@ -215,14 +217,22 @@ class Order extends BaseAction implements EventSubscriberInterface
->setWasNew($pse->getNewness())
->setWasInPromo($cartItem->getPromo())
->setWeight($pse->getWeight())
->setTaxRuleTitle($taxRuleI18n->getTitle())
->setTaxRuleDescription($taxRuleI18n->getDescription())
;
$orderProduct->setDispatcher($this->getDispatcher());
$orderProduct->save();
$orderProduct->save($con);
/* fulfill order_product_tax */
foreach($taxDetail as $tax) {
$tax->setOrderProductId($orderProduct->getId());
$tax->save($con);
}
/* fulfill order_attribute_combination and decrease stock */
foreach($pse->getAttributeCombinations() as $attributeCombination) {
$attribute = $this->getI18n(AttributeI18nQuery::create(), new AttributeI18n(), $attributeCombination->getAttributeId());
$attributeAv = $this->getI18n(AttributeAvI18nQuery::create(), new AttributeAvI18n(), $attributeCombination->getAttributeAvId());
$attribute = I18n::forceI18nRetrieving($this->getSession()->getLang()->getLocale(), 'Attribute', $attributeCombination->getAttributeId());
$attributeAv = I18n::forceI18nRetrieving($this->getSession()->getLang()->getLocale(), 'AttributeAv', $attributeCombination->getAttributeAvId());
$orderAttributeCombination = new OrderProductAttributeCombination();
$orderAttributeCombination
@@ -237,7 +247,7 @@ class Order extends BaseAction implements EventSubscriberInterface
->setAttributeAvPostscriptum($attributeAv->getPostscriptum())
;
$orderAttributeCombination->save();
$orderAttributeCombination->save($con);
}
}
@@ -248,12 +258,13 @@ class Order extends BaseAction implements EventSubscriberInterface
$this->getDispatcher()->dispatch(TheliaEvents::ORDER_BEFORE_PAYMENT, new OrderEvent($placedOrder));
/* clear session */
/* but memorize placed order */
$sessionOrder = new \Thelia\Model\Order();
$event->setOrder($sessionOrder);
$event->setPlacedOrder($placedOrder);
$this->getSession()->setOrder($sessionOrder);
/* empty cart */
$this->getSession()->getCart()->clear();
/* empty cart @todo */
/* call pay method */
$paymentModuleReflection = new \ReflectionClass($paymentModule->getFullNamespace());
@@ -349,42 +360,4 @@ class Order extends BaseAction implements EventSubscriberInterface
return $request->getSession();
}
/**
* @param ModelCriteria $query
* @param ActiveRecordInterface $object
* @param $id
* @param array $needed = array('Title')
*
* @return ProductI18n
*/
protected function getI18n(ModelCriteria $query, ActiveRecordInterface $object, $id, $needed = array('Title'))
{
$i18n = $query
->filterById($id)
->filterByLocale(
$this->getSession()->getLang()->getLocale()
)->findOne();
/* or default translation */
if(null === $i18n) {
$i18n = $query
->filterById($id)
->filterByLocale(
Lang::getDefaultLanguage()->getLocale()
)->findOne();
}
if(null === $i18n) { // @todo something else ?
$i18n = $object;
foreach($needed as $need) {
$method = sprintf('get%s', $need);
if(method_exists($i18n, $method)) {
$i18n->$method('DEFAULT ' . strtoupper($need));
} else {
// @todo throw sg ?
}
}
}
return $i18n;
}
}

View File

@@ -21,6 +21,7 @@
<loop class="Thelia\Core\Template\Loop\FeatureAvailability" name="feature_availability"/>
<loop class="Thelia\Core\Template\Loop\FeatureValue" name="feature_value"/>
<loop class="Thelia\Core\Template\Loop\Folder" name="folder"/>
<loop class="Thelia\Core\Template\Loop\Module" name="module"/>
<loop class="Thelia\Core\Template\Loop\Order" name="order"/>
<loop class="Thelia\Core\Template\Loop\OrderStatus" name="order-status"/>
<loop class="Thelia\Core\Template\Loop\CategoryPath" name="category-path"/>

View File

@@ -137,7 +137,11 @@
<route id="order.payment.process" path="/order/pay">
<default key="_controller">Thelia\Controller\Front\OrderController::pay</default>
<default key="_view">order_payment</default>
</route>
<route id="order.placed" path="/order/placed/{order_id}">
<default key="_controller">Thelia\Controller\Front\OrderController::orderPlaced</default>
<default key="_view">order_placed</default>
</route>
<!-- end order management process -->

View File

@@ -23,6 +23,7 @@
namespace Thelia\Controller\Front;
use Propel\Runtime\Exception\PropelException;
use Thelia\Exception\TheliaProcessException;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Core\Event\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
@@ -32,9 +33,11 @@ use Thelia\Form\OrderPayment;
use Thelia\Log\Tlog;
use Thelia\Model\AddressQuery;
use Thelia\Model\AreaDeliveryModuleQuery;
use Thelia\Model\Base\OrderQuery;
use Thelia\Model\CountryQuery;
use Thelia\Model\ModuleQuery;
use Thelia\Model\Order;
use Thelia\Tools\URL;
/**
* Class OrderController
@@ -187,6 +190,36 @@ class OrderController extends BaseFrontController
$orderEvent = $this->getOrderEvent();
$this->getDispatcher()->dispatch(TheliaEvents::ORDER_PAY, $orderEvent);
$placedOrder = $orderEvent->getPlacedOrder();
if(null !== $placedOrder && null !== $placedOrder->getId()) {
/* order has been placed */
$this->redirect(URL::getInstance()->absoluteUrl($this->getRoute('order.placed', array('order_id' => $orderEvent->getPlacedOrder()->getId()))));
} else {
/* order has not been placed */
$this->redirectToRoute("cart.view");
}
}
public function orderPlaced($order_id)
{
/* check if the placed order matched the customer */
$placedOrder = OrderQuery::create()->findPk(
$this->getRequest()->attributes->get('order_id')
);
if(null === $placedOrder) {
throw new TheliaProcessException("No placed order", TheliaProcessException::NO_PLACED_ORDER, $placedOrder);
}
$customer = $this->getSecurityContext()->getCustomerUser();
if(null === $customer || $placedOrder->getCustomerId() !== $customer->getId()) {
throw new TheliaProcessException("Received placed order id does not belong to the current customer", TheliaProcessException::PLACED_ORDER_ID_BAD_CURRENT_CUSTOMER, $placedOrder);
}
$this->getParserContext()->set("placed_order_id", $placedOrder->getId());
}
protected function getOrderEvent()

View File

@@ -28,6 +28,7 @@ use Thelia\Model\Order;
class OrderEvent extends ActionEvent
{
protected $order = null;
protected $placedOrder = null;
protected $invoiceAddress = null;
protected $deliveryAddress = null;
protected $deliveryModule = null;
@@ -51,6 +52,14 @@ class OrderEvent extends ActionEvent
$this->order = $order;
}
/**
* @param Order $order
*/
public function setPlacedOrder(Order $order)
{
$this->placedOrder = $order;
}
/**
* @param $address
*/
@@ -107,6 +116,14 @@ class OrderEvent extends ActionEvent
return $this->order;
}
/**
* @return null|Order
*/
public function getPlacedOrder()
{
return $this->placedOrder;
}
/**
* @return null|int
*/

View File

@@ -81,6 +81,8 @@ class Cart extends BaseLoop
return $result;
}
$taxCountry = CountryQuery::create()->findPk(64); // @TODO : make it magic;
foreach ($cartItems as $cartItem) {
$product = $cartItem->getProduct();
$productSaleElement = $cartItem->getProductSaleElements();
@@ -97,12 +99,8 @@ class Cart extends BaseLoop
->set("STOCK", $productSaleElement->getQuantity())
->set("PRICE", $cartItem->getPrice())
->set("PROMO_PRICE", $cartItem->getPromoPrice())
->set("TAXED_PRICE", $cartItem->getTaxedPrice(
CountryQuery::create()->findOneById(64) // @TODO : make it magic
))
->set("PROMO_TAXED_PRICE", $cartItem->getTaxedPromoPrice(
CountryQuery::create()->findOneById(64) // @TODO : make it magic
))
->set("TAXED_PRICE", $cartItem->getTaxedPrice($taxCountry))
->set("PROMO_TAXED_PRICE", $cartItem->getTaxedPromoPrice($taxCountry))
->set("IS_PROMO", $cartItem->getPromo() === 1 ? 1 : 0);
$result->addRow($loopResultRow);
}

View File

@@ -0,0 +1,137 @@
<?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\Core\Template\Loop;
use Propel\Runtime\ActiveQuery\Criteria;
use Thelia\Core\Template\Element\BaseI18nLoop;
use Thelia\Core\Template\Element\LoopResult;
use Thelia\Core\Template\Element\LoopResultRow;
use Thelia\Core\Template\Loop\Argument\ArgumentCollection;
use Thelia\Core\Template\Loop\Argument\Argument;
use Thelia\Model\ModuleQuery;
use Thelia\Module\BaseModule;
use Thelia\Type;
/**
*
* Module loop
*
*
* Class Module
* @package Thelia\Core\Template\Loop
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class Module extends BaseI18nLoop
{
public $timestampable = true;
/**
* @return ArgumentCollection
*/
protected function getArgDefinitions()
{
return new ArgumentCollection(
Argument::createIntListTypeArgument('id'),
new Argument(
'module_type',
new Type\TypeCollection(
new Type\EnumListType(array(
BaseModule::CLASSIC_MODULE_TYPE,
BaseModule::DELIVERY_MODULE_TYPE,
BaseModule::PAYMENT_MODULE_TYPE,
))
)
),
Argument::createIntListTypeArgument('exclude'),
Argument::createBooleanOrBothTypeArgument('active', Type\BooleanOrBothType::ANY)
);
}
/**
* @param $pagination
*
* @return \Thelia\Core\Template\Element\LoopResult
*/
public function exec(&$pagination)
{
$search = ModuleQuery::create();
/* manage translations */
$locale = $this->configureI18nProcessing($search);
$id = $this->getId();
if (null !== $id) {
$search->filterById($id, Criteria::IN);
}
$moduleType = $this->getModule_type();
if (null !== $moduleType) {
$search->filterByType($moduleType, Criteria::IN);
}
$exclude = $this->getExclude();
if (!is_null($exclude)) {
$search->filterById($exclude, Criteria::NOT_IN);
}
$active = $this->getActive();
if($active !== Type\BooleanOrBothType::ANY) {
$search->filterByActivate($active ? 1 : 0, Criteria::EQUAL);
}
$search->orderByPosition();
/* perform search */
$modules = $this->search($search, $pagination);
$loopResult = new LoopResult($modules);
foreach ($modules as $module) {
$loopResultRow = new LoopResultRow($loopResult, $module, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("ID", $module->getId())
->set("IS_TRANSLATED",$module->getVirtualColumn('IS_TRANSLATED'))
->set("LOCALE",$locale)
->set("TITLE",$module->getVirtualColumn('i18n_TITLE'))
->set("CHAPO", $module->getVirtualColumn('i18n_CHAPO'))
->set("DESCRIPTION", $module->getVirtualColumn('i18n_DESCRIPTION'))
->set("POSTSCRIPTUM", $module->getVirtualColumn('i18n_POSTSCRIPTUM'))
->set("CODE", $module->getCode())
->set("TYPE", $module->getType())
->set("ACTIVE", $module->getActivate())
->set("CLASS", $module->getFullNamespace())
->set("POSITION", $module->getPosition());
$loopResult->addRow($loopResultRow);
}
return $loopResult;
}
}

View File

@@ -23,12 +23,16 @@
namespace Thelia\Core\Template\Loop;
use Propel\Runtime\ActiveQuery\Criteria;
use Thelia\Core\Template\Element\BaseLoop;
use Thelia\Core\Template\Element\LoopResult;
use Thelia\Core\Template\Element\LoopResultRow;
use Thelia\Core\Template\Loop\Argument\ArgumentCollection;
use Thelia\Core\Template\Loop\Argument\Argument;
use Thelia\Model\OrderQuery;
use Thelia\Type\TypeCollection;
use Thelia\Type;
/**
*
* @package Thelia\Core\Template\Loop
@@ -37,19 +41,94 @@ use Thelia\Core\Template\Loop\Argument\Argument;
*/
class Order extends BaseLoop
{
public $countable = true;
public $timestampable = true;
public $versionable = false;
public function getArgDefinitions()
{
return new ArgumentCollection();
return new ArgumentCollection(
Argument::createIntListTypeArgument('id'),
new Argument(
'customer',
new TypeCollection(
new Type\IntType(),
new Type\EnumType(array('current'))
),
'current'
),
Argument::createIntListTypeArgument('status')
);
}
/**
* @param $pagination
*
*
* @return \Thelia\Core\Template\Element\LoopResult
* @return LoopResult
*/
public function exec(&$pagination)
{
// TODO : a coder !
$search = OrderQuery::create();
$id = $this->getId();
if (null !== $id) {
$search->filterById($id, Criteria::IN);
}
$customer = $this->getCustomer();
if ($customer === 'current') {
$currentCustomer = $this->securityContext->getCustomerUser();
if ($currentCustomer === null) {
return new LoopResult();
} else {
$search->filterByCustomerId($currentCustomer->getId(), Criteria::EQUAL);
}
} else {
$search->filterByCustomerId($customer, Criteria::EQUAL);
}
$status = $this->getStatus();
if (null !== $status) {
$search->filterByStatusId($status, Criteria::IN);
}
$orders = $this->search($search, $pagination);
$loopResult = new LoopResult($orders);
foreach ($orders as $order) {
$tax = 0;
$amount = $order->getTotalAmount($tax);
$loopResultRow = new LoopResultRow($loopResult, $order, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow
->set("ID", $order->getId())
->set("REF", $order->getRef())
->set("CUSTOMER", $order->getCustomerId())
->set("DELIVERY_ADDRESS", $order->getDeliveryOrderAddressId())
->set("INVOICE_ADDRESS", $order->getInvoiceOrderAddressId())
->set("INVOICE_DATE", $order->getInvoiceDate())
->set("CURRENCY", $order->getCurrencyId())
->set("CURRENCY_RATE", $order->getCurrencyRate())
->set("TRANSACTION_REF", $order->getTransactionRef())
->set("DELIVERY_REF", $order->getDeliveryRef())
->set("INVOICE_REF", $order->getInvoiceRef())
->set("POSTAGE", $order->getPostage())
->set("PAYMENT_MODULE", $order->getPaymentModuleId())
->set("DELIVERY_MODULE", $order->getDeliveryModuleId())
->set("STATUS", $order->getStatusId())
->set("LANG", $order->getLangId())
->set("POSTAGE", $order->getPostage())
->set("TOTAL_TAX", $tax)
->set("TOTAL_AMOUNT", $amount - $tax)
->set("TOTAL_TAXED_AMOUNT", $amount)
;
$loopResult->addRow($loopResultRow);
}
return $loopResult;
}
}

View File

@@ -30,7 +30,6 @@ class OrderException extends \RuntimeException
*/
public $cartRoute = "cart.view";
public $orderDeliveryRoute = "order.delivery";
public $cartItem = null;
public $arguments = array();
@@ -40,8 +39,6 @@ class OrderException extends \RuntimeException
const UNDEFINED_DELIVERY = 200;
const NOT_ENOUGH_STOCK = 300;
public function __construct($message, $code = null, $arguments = array(), $previous = null)
{
if(is_array($arguments)) {

View File

@@ -39,6 +39,7 @@ class TaxEngineException extends \RuntimeException
const UNDEFINED_TAX_RULES_COLLECTION = 503;
const UNDEFINED_REQUIREMENTS = 504;
const UNDEFINED_REQUIREMENT_VALUE = 505;
const UNDEFINED_TAX_RULE = 506;
const BAD_AMOUNT_FORMAT = 601;

View File

@@ -0,0 +1,54 @@
<?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\Exception;
/**
* these exception are non fatal exception, due to thelia process exception
* or customer random navigation
*
* they redirect the customer who trig them to a specific error page // @todo
*
* Class TheliaProcessException
* @package Thelia\Exception
*/
class TheliaProcessException extends \RuntimeException
{
public $data = null;
const UNKNOWN_EXCEPTION = 0;
const CART_ITEM_NOT_ENOUGH_STOCK = 100;
const NO_PLACED_ORDER = 101;
const PLACED_ORDER_ID_BAD_CURRENT_CUSTOMER = 102;
public function __construct($message, $code = null, $data = null, $previous = null)
{
$this->data = $data;
if ($code === null) {
$code = self::UNKNOWN_EXCEPTION;
}
parent::__construct($message, $code, $previous);
}
}

View File

@@ -2,11 +2,15 @@
namespace Thelia\Model;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\Propel;
use Thelia\Core\Event\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Base\Order as BaseOrder;
use Thelia\Model\Base\OrderProductTaxQuery;
use Thelia\Model\Map\OrderProductTaxTableMap;
use Thelia\Model\OrderProductQuery;
use Thelia\Model\Map\OrderTableMap;
use \PDO;
@@ -38,13 +42,27 @@ class Order extends BaseOrder
/**
* calculate the total amount
*
* @TODO create body method
* @param int $tax
*
* @return int
* @return int|string|Base\double
*/
public function getTotalAmount()
public function getTotalAmount(&$tax = 0)
{
return 2;
$amount = 0;
$tax = 0;
/* browse all products */
$orderProductIds = array();
foreach($this->getOrderProducts() as $orderProduct) {
$taxAmount = OrderProductTaxQuery::create()
->withColumn('SUM(' . OrderProductTaxTableMap::AMOUNT . ')', 'total_tax')
->filterByOrderProductId($orderProduct->getId(), Criteria::EQUAL)
->findOne();
$amount += ($orderProduct->getWasInPromo() == 1 ? $orderProduct->getPromoPrice() : $orderProduct->getPrice()) * $orderProduct->getQuantity();
$tax += round($taxAmount->getVirtualColumn('total_tax'), 2) * $orderProduct->getQuantity();
}
return $amount + $tax + $this->getPostage(); // @todo : manage discount
}
/**

View File

@@ -2,8 +2,11 @@
namespace Thelia\Model;
use Propel\Runtime\Exception\PropelException;
use Propel\Runtime\Propel;
use Thelia\Model\Base\OrderQuery as BaseOrderQuery;
use \PDO;
use Thelia\Model\Map\OrderTableMap;
/**
* Skeleton subclass for performing query and update operations on the 'order' table.
@@ -15,6 +18,38 @@ use Thelia\Model\Base\OrderQuery as BaseOrderQuery;
* long as it does not already exist in the output directory.
*
*/
class OrderQuery extends BaseOrderQuery {
class OrderQuery extends BaseOrderQuery
{
/**
* PROPEL SHOULD FIX IT
*
* Find object by primary key using raw SQL to go fast.
* Bypass doSelect() and the object formatter by using generated code.
*
* @param mixed $key Primary key to use for the query
* @param ConnectionInterface $con A connection object
*
* @return Order A model object, or null if the key is not found
*/
protected function findPkSimple($key, $con)
{
$sql = 'SELECT ID, REF, CUSTOMER_ID, INVOICE_ORDER_ADDRESS_ID, DELIVERY_ORDER_ADDRESS_ID, INVOICE_DATE, CURRENCY_ID, CURRENCY_RATE, TRANSACTION_REF, DELIVERY_REF, INVOICE_REF, POSTAGE, PAYMENT_MODULE_ID, DELIVERY_MODULE_ID, STATUS_ID, LANG_ID, CREATED_AT, UPDATED_AT FROM `order` WHERE ID = :p0';
try {
$stmt = $con->prepare($sql);
$stmt->bindValue(':p0', $key, PDO::PARAM_INT);
$stmt->execute();
} catch (\Exception $e) {
Propel::log($e->getMessage(), Propel::LOG_ERR);
throw new PropelException(sprintf('Unable to execute SELECT statement [%s]', $sql), 0, $e);
}
$obj = null;
if ($row = $stmt->fetch(\PDO::FETCH_NUM)) {
$obj = new Order();
$obj->hydrate($row);
OrderTableMap::addInstanceToPool($obj, (string) $key);
}
$stmt->closeCursor();
return $obj;
}
} // OrderQuery

View File

@@ -3,7 +3,25 @@
namespace Thelia\Model;
use Thelia\Model\Base\TaxRule as BaseTaxRule;
use Thelia\TaxEngine\Calculator;
use Thelia\TaxEngine\OrderProductTaxCollection;
class TaxRule extends BaseTaxRule {
class TaxRule extends BaseTaxRule
{
/**
* @param Country $country
* @param $untaxedAmount
* @param null $askedLocale
*
* @return OrderProductTaxCollection
*/
public function getTaxDetail(Country $country, $untaxedAmount, $askedLocale = null)
{
$taxCalculator = new Calculator();
$taxCollection = new OrderProductTaxCollection();
$taxCalculator->loadTaxRule($this, $country)->getTaxedPrice($untaxedAmount, $taxCollection, $askedLocale);
return $taxCollection;
}
}

View File

@@ -21,13 +21,19 @@ class TaxRuleQuery extends BaseTaxRuleQuery
{
const ALIAS_FOR_TAX_RULE_COUNTRY_POSITION = 'taxRuleCountryPosition';
public function getTaxCalculatorCollection(Product $product, Country $country)
/**
* @param TaxRule $taxRule
* @param Country $country
*
* @return array|mixed|\Propel\Runtime\Collection\ObjectCollection
*/
public function getTaxCalculatorCollection(TaxRule $taxRule, Country $country)
{
$search = TaxQuery::create()
->filterByTaxRuleCountry(
TaxRuleCountryQuery::create()
->filterByCountry($country, Criteria::EQUAL)
->filterByTaxRuleId($product->getTaxRuleId())
->filterByTaxRuleId($taxRule->getId())
->orderByPosition()
->find()
)

View File

@@ -25,7 +25,9 @@
namespace Thelia\Module;
use Symfony\Component\DependencyInjection\ContainerAware;
use Thelia\Model\ModuleI18nQuery;
use Thelia\Model\Map\ModuleImageTableMap;
use Thelia\Model\ModuleI18n;
use Thelia\Tools\Image;
use Thelia\Exception\ModuleException;
use Thelia\Model\Module;
@@ -76,6 +78,27 @@ abstract class BaseModule extends ContainerAware
return $this->container;
}
public function setTitle(Module $module, $titles)
{
if(is_array($titles)) {
foreach($titles as $locale => $title) {
$moduleI18n = ModuleI18nQuery::create()->filterById($module->getId())->filterByLocale($locale)->findOne();
if(null === $moduleI18n) {
$moduleI18n = new ModuleI18n();
$moduleI18n
->setId($module->getId())
->setLocale($locale)
->setTitle($title)
;
$moduleI18n->save();
} else {
$moduleI18n->setTitle($title);
$moduleI18n->save();
}
}
}
}
public function deployImageFolder(Module $module, $folderPath)
{
try {

View File

@@ -24,8 +24,11 @@ namespace Thelia\TaxEngine;
use Thelia\Exception\TaxEngineException;
use Thelia\Model\Country;
use Thelia\Model\OrderProductTax;
use Thelia\Model\Product;
use Thelia\Model\TaxRule;
use Thelia\Model\TaxRuleQuery;
use Thelia\Tools\I18n;
/**
* Class Calculator
@@ -68,14 +71,34 @@ class Calculator
$this->product = $product;
$this->country = $country;
$this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($product, $country);
$this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($product->getTaxRule(), $country);
return $this;
}
public function getTaxAmountFromUntaxedPrice($untaxedPrice)
public function loadTaxRule(TaxRule $taxRule, Country $country)
{
return $this->getTaxedPrice($untaxedPrice) - $untaxedPrice;
$this->product = null;
$this->country = null;
$this->taxRulesCollection = null;
if($taxRule->getId() === null) {
throw new TaxEngineException('TaxRule id is empty in Calculator::loadTaxRule', TaxEngineException::UNDEFINED_TAX_RULE);
}
if($country->getId() === null) {
throw new TaxEngineException('Country id is empty in Calculator::loadTaxRule', TaxEngineException::UNDEFINED_COUNTRY);
}
$this->country = $country;
$this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($taxRule, $country);
return $this;
}
public function getTaxAmountFromUntaxedPrice($untaxedPrice, &$taxCollection = null)
{
return $this->getTaxedPrice($untaxedPrice, $taxCollection) - $untaxedPrice;
}
public function getTaxAmountFromTaxedPrice($taxedPrice)
@@ -83,7 +106,15 @@ class Calculator
return $taxedPrice - $this->getUntaxedPrice($taxedPrice);
}
public function getTaxedPrice($untaxedPrice)
/**
* @param $untaxedPrice
* @param null $taxCollection returns OrderProductTaxCollection
* @param null $askedLocale
*
* @return int
* @throws \Thelia\Exception\TaxEngineException
*/
public function getTaxedPrice($untaxedPrice, &$taxCollection = null, $askedLocale = null)
{
if(null === $this->taxRulesCollection) {
throw new TaxEngineException('Tax rules collection is empty in Calculator::getTaxAmount', TaxEngineException::UNDEFINED_TAX_RULES_COLLECTION);
@@ -97,6 +128,9 @@ class Calculator
$currentPosition = 1;
$currentTax = 0;
if(null !== $taxCollection) {
$taxCollection = new OrderProductTaxCollection();
}
foreach($this->taxRulesCollection as $taxRule) {
$position = (int)$taxRule->getTaxRuleCountryPosition();
@@ -109,7 +143,17 @@ class Calculator
$currentPosition = $position;
}
$currentTax += $taxType->calculate($taxedPrice);
$taxAmount = round($taxType->calculate($taxedPrice), 2);
$currentTax += $taxAmount;
if(null !== $taxCollection) {
$taxI18n = I18n::forceI18nRetrieving($askedLocale, 'Tax', $taxRule->getId());
$orderProductTax = new OrderProductTax();
$orderProductTax->setTitle($taxI18n->getTitle());
$orderProductTax->setDescription($taxI18n->getDescription());
$orderProductTax->setAmount($taxAmount);
$taxCollection->addTax($orderProductTax);
}
}
$taxedPrice += $currentTax;

View File

@@ -0,0 +1,126 @@
<?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\Model\OrderProductTax;
/**
*
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*
*/
class OrderProductTaxCollection implements \Iterator
{
private $position;
protected $taxes = array();
public function __construct()
{
foreach (func_get_args() as $tax) {
$this->addTax($tax);
}
}
public function isEmpty()
{
return count($this->taxes) == 0;
}
/**
* @param OrderProductTax $tax
*
* @return OrderProductTaxCollection
*/
public function addTax(OrderProductTax $tax)
{
$this->taxes[] = $tax;
return $this;
}
public function getCount()
{
return count($this->taxes);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Return the current element
* @link http://php.net/manual/en/iterator.current.php
* @return OrderProductTax
*/
public function current()
{
return $this->taxes[$this->position];
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Move forward to next element
* @link http://php.net/manual/en/iterator.next.php
* @return void Any returned value is ignored.
*/
public function next()
{
$this->position++;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Return the key of the current element
* @link http://php.net/manual/en/iterator.key.php
* @return mixed scalar on success, or null on failure.
*/
public function key()
{
return $this->position;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Checks if current position is valid
* @link http://php.net/manual/en/iterator.valid.php
* @return boolean The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid()
{
return isset($this->taxes[$this->position]);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Rewind the Iterator to the first element
* @link http://php.net/manual/en/iterator.rewind.php
* @return void Any returned value is ignored.
*/
public function rewind()
{
$this->position = 0;
}
public function getKey($key)
{
return isset($this->taxes[$key]) ? $this->taxes[$key] : null;
}
}

View File

@@ -86,7 +86,7 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase
$taxRuleQuery = $this->getMock('\Thelia\Model\TaxRuleQuery', array('getTaxCalculatorCollection'));
$taxRuleQuery->expects($this->once())
->method('getTaxCalculatorCollection')
->with($productQuery, $countryQuery)
->with($productQuery->getTaxRule(), $countryQuery)
->will($this->returnValue('foo'));
$rewritingUrlQuery = $this->getProperty('taxRuleQuery');

View File

@@ -23,6 +23,8 @@
namespace Thelia\Tools;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
use Thelia\Model\Lang;
/**
@@ -54,4 +56,39 @@ class I18n
return \DateTime::createFromFormat($currentDateFormat, $date);
}
public static function forceI18nRetrieving($askedLocale, $modelName, $id, $needed = array('Title'))
{
$i18nQueryClass = sprintf("\\Thelia\\Model\\%sI18nQuery", $modelName);
$i18nClass = sprintf("\\Thelia\\Model\\%sI18n", $modelName);
/* get customer language translation */
$i18n = $i18nQueryClass::create()
->filterById($id)
->filterByLocale(
$askedLocale
)->findOne();
/* or default translation */
if(null === $i18n) {
$i18n = $i18nQueryClass::create()
->filterById($id)
->filterByLocale(
Lang::getDefaultLanguage()->getLocale()
)->findOne();
}
if(null === $i18n) { // @todo something else ?
$i18n = new $i18nClass();;
$i18n->setId($id);
foreach($needed as $need) {
$method = sprintf('set%s', $need);
if(method_exists($i18n, $method)) {
$i18n->$method('DEFAULT ' . strtoupper($need));
} else {
// @todo throw sg ?
}
}
}
return $i18n;
}
}

View File

@@ -1153,7 +1153,7 @@ INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `created_at`, `upda
INSERT INTO `tax_i18n` (`id`, `locale`, `title`)
VALUES
(1, 'fr_FR', 'TVA française à 19.6%'),
(1, 'en_UK', 'french 19.6% tax');
(1, 'en_US', 'french 19.6% tax');
INSERT INTO `tax_rule` (`id`, `is_default`, `created_at`, `updated_at`)
VALUES
@@ -1162,7 +1162,7 @@ INSERT INTO `tax_rule` (`id`, `is_default`, `created_at`, `updated_at`)
INSERT INTO `tax_rule_i18n` (`id`, `locale`, `title`)
VALUES
(1, 'fr_FR', 'TVA française à 19.6%'),
(1, 'en_UK', 'french 19.6% tax');
(1, 'en_US', 'french 19.6% tax');
INSERT INTO `tax_rule_country` (`tax_rule_id`, `country_id`, `tax_id`, `position`, `created_at`, `updated_at`)
VALUES

View File

@@ -71,6 +71,15 @@ class Cheque extends BaseModule implements PaymentModuleInterface
if(ModuleImageQuery::create()->filterByModule($module)->count() == 0) {
$this->deployImageFolder($module, sprintf('%s/images', __DIR__));
}
/* set module title */
$this->setTitle(
$module,
array(
"en_US" => "Cheque",
"fr_FR" => "Cheque",
)
);
}
public function destroy()

View File

@@ -71,6 +71,15 @@ class FakeCB extends BaseModule implements PaymentModuleInterface
if(ModuleImageQuery::create()->filterByModule($module)->count() == 0) {
$this->deployImageFolder($module, sprintf('%s/images', __DIR__));
}
/* set module title */
$this->setTitle(
$module,
array(
"en_US" => "Credit Card",
"fr_FR" => "Credit Card",
)
);
}
public function destroy()

View File

@@ -24,9 +24,11 @@
<a href="cart-step4.php" role="button" class="btn btn-step active"><span class="step-nb">4</span> <span class="step-label">Secure payment</span></a>
</div>
{loop type="order" name="placed-order" id=$placed_order_id}
<div id="payment-success" class="panel">
<div class="panel-heading">
<h3 class="panel-title">You chose to pay by : <span class="payment-method">Cheque</span></h3>
<h3 class="panel-title">You chose to pay by : <span class="payment-method">{loop name="payment-module" type="module" id=$PAYMENT_MODULE}{$TITLE}{/loop}</span></h3>
</div>
<div class="panel-body">
<h3>Thank you for the trust you place in us.</h3>
@@ -35,15 +37,17 @@
<dl class="dl-horizontal">
<dt>Order number : </dt>
<dd>PRO123456788978979</dd>
<dd>{$REF}</dd>
<dt>Date : </dt>
<dd>02/12/2013</dd>
<dd>{format_date date=$CREATE_DATE output="date"}</dd>
<dt>Total : </dt>
<dd>$216.25</dd>
<dd>{loop type="currency" name="order-currency" id=$CURRENCY}{$SYMBOL}{/loop} {$TOTAL_TAXED_AMOUNT}</dd>
</dl>
</div>
</div>
{/loop}
<a href="{navigate to="index"}" role="button" class="btn btn-checkout-home"><span>Go home</span></a>
</article>