Initial commit
This commit is contained in:
539
core/lib/Thelia/Model/Order.php
Normal file
539
core/lib/Thelia/Model/Order.php
Normal file
@@ -0,0 +1,539 @@
|
||||
<?php
|
||||
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Thelia\Core\Event\Order\OrderEvent;
|
||||
use Thelia\Core\Event\Payment\ManageStockOnCreationEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Exception\TheliaProcessException;
|
||||
use Thelia\Model\Base\Order as BaseOrder;
|
||||
use Thelia\Model\Map\OrderProductTableMap;
|
||||
use Thelia\Model\Map\OrderProductTaxTableMap;
|
||||
use Thelia\Model\Tools\ModelEventDispatcherTrait;
|
||||
use Thelia\TaxEngine\Calculator;
|
||||
|
||||
class Order extends BaseOrder
|
||||
{
|
||||
use ModelEventDispatcherTrait;
|
||||
|
||||
/** @var int|null */
|
||||
protected $choosenDeliveryAddress = null;
|
||||
/** @var int|null */
|
||||
protected $choosenInvoiceAddress = null;
|
||||
|
||||
protected $disableVersioning = false;
|
||||
|
||||
/**
|
||||
* @param int $choosenDeliveryAddress the choosen delivery address ID
|
||||
* @return $this
|
||||
*/
|
||||
public function setChoosenDeliveryAddress($choosenDeliveryAddress)
|
||||
{
|
||||
$this->choosenDeliveryAddress = $choosenDeliveryAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $disableVersioning
|
||||
* @return $this
|
||||
*/
|
||||
public function setDisableVersioning($disableVersioning)
|
||||
{
|
||||
$this->disableVersioning = (bool) $disableVersioning;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isVersioningDisable()
|
||||
{
|
||||
return $this->disableVersioning;
|
||||
}
|
||||
|
||||
public function isVersioningNecessary($con = null)
|
||||
{
|
||||
if ($this->isVersioningDisable()) {
|
||||
return false;
|
||||
} else {
|
||||
return parent::isVersioningNecessary($con);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null the choosen delivery address ID
|
||||
*/
|
||||
public function getChoosenDeliveryAddress()
|
||||
{
|
||||
return $this->choosenDeliveryAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $choosenInvoiceAddress the choosen invoice address
|
||||
* @return $this
|
||||
*/
|
||||
public function setChoosenInvoiceAddress($choosenInvoiceAddress)
|
||||
{
|
||||
$this->choosenInvoiceAddress = $choosenInvoiceAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null the choosen invoice address ID
|
||||
*/
|
||||
public function getChoosenInvoiceAddress()
|
||||
{
|
||||
return $this->choosenInvoiceAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function preSave(ConnectionInterface $con = null)
|
||||
{
|
||||
if ($this->isPaid(false) && null === $this->getInvoiceDate()) {
|
||||
$this
|
||||
->setInvoiceDate(time());
|
||||
}
|
||||
|
||||
return parent::preSave($con);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
parent::preInsert($con);
|
||||
|
||||
$this->dispatchEvent(TheliaEvents::ORDER_BEFORE_CREATE, new OrderEvent($this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConnectionInterface|null $con
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function postInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
parent::postInsert($con);
|
||||
|
||||
$this->setRef($this->generateRef())
|
||||
->setDisableVersioning(true)
|
||||
->save($con);
|
||||
$this->dispatchEvent(TheliaEvents::ORDER_AFTER_CREATE, new OrderEvent($this));
|
||||
}
|
||||
|
||||
public function generateRef()
|
||||
{
|
||||
return sprintf('ORD%s', str_pad($this->getId(), 12, 0, STR_PAD_LEFT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute this order amount with taxes. The tax amount is returned in the $tax parameter.
|
||||
*
|
||||
* The order amount is only available once the order is persisted in database.
|
||||
* During invoice process, use all cart methods instead of order methods (the order doest not exists at this moment)
|
||||
*
|
||||
* @param float|int $tax (output only) returns the tax amount for this order
|
||||
* @param bool $includePostage if true, the postage cost is included to the total
|
||||
* @param bool $includeDiscount if true, the discount will be included to the total
|
||||
* @return float
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function getTotalAmount(&$tax = 0, $includePostage = true, $includeDiscount = true)
|
||||
{
|
||||
// To prevent price changes in pre-2.4 orders, use the legacy calculation method
|
||||
if ($this->getId() <= ConfigQuery::read('last_legacy_rounding_order_id', 0)) {
|
||||
return $this->getTotalAmountLegacy($tax, $includePostage, $includeDiscount);
|
||||
}
|
||||
|
||||
// Cache the query result. Wa have to une and array indexed on the order ID, as the cache ios static
|
||||
// and may cache results for several orders, for example in the order list in the back-office.
|
||||
static $queryResult = [];
|
||||
|
||||
$id = $this->getId();
|
||||
|
||||
if (!isset($queryResult[$id]) || null === $queryResult[$id]) {
|
||||
// Shoud be the same rounding method as in CartItem::getTotalTaxedPrice()
|
||||
// For each order line, we round quantity x taxed price.
|
||||
$query = '
|
||||
SELECT
|
||||
SUM(
|
||||
'.OrderProductTableMap::COL_QUANTITY.'
|
||||
*
|
||||
ROUND(
|
||||
(
|
||||
IF('.OrderProductTableMap::COL_WAS_IN_PROMO.'=1, '.OrderProductTableMap::COL_PROMO_PRICE.', '.OrderProductTableMap::COL_PRICE.')
|
||||
+
|
||||
(
|
||||
SELECT COALESCE(SUM(IF('.OrderProductTableMap::COL_WAS_IN_PROMO.'=1, '.OrderProductTaxTableMap::COL_PROMO_AMOUNT.', '.OrderProductTaxTableMap::COL_AMOUNT.')), 0)
|
||||
FROM '.OrderProductTaxTableMap::TABLE_NAME.'
|
||||
WHERE '.OrderProductTaxTableMap::COL_ORDER_PRODUCT_ID.' = '.OrderProductTableMap::COL_ID.'
|
||||
)
|
||||
), 2
|
||||
)
|
||||
) as total_taxed_price,
|
||||
SUM(
|
||||
'.OrderProductTableMap::COL_QUANTITY.'
|
||||
*
|
||||
ROUND(
|
||||
IF(
|
||||
'.OrderProductTableMap::COL_WAS_IN_PROMO.'=1,
|
||||
'.OrderProductTableMap::COL_PROMO_PRICE.',
|
||||
'.OrderProductTableMap::COL_PRICE.'
|
||||
), 2
|
||||
)
|
||||
) as total_untaxed_price
|
||||
from
|
||||
'.OrderProductTableMap::TABLE_NAME.'
|
||||
where
|
||||
'.OrderProductTableMap::COL_ORDER_ID.'=:order_id
|
||||
';
|
||||
|
||||
$con = Propel::getConnection();
|
||||
$stmt = $con->prepare($query);
|
||||
|
||||
if (false === $stmt->execute([':order_id' => $this->getId()])) {
|
||||
throw new TheliaProcessException(
|
||||
sprintf(
|
||||
'Failed to get order total and order tax: %s (%s)',
|
||||
$stmt->errorInfo(),
|
||||
$stmt->errorCode()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$queryResult[$id] = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
$total = (float) $queryResult[$id]->total_taxed_price;
|
||||
$tax = $total - (float) $queryResult[$id]->total_untaxed_price;
|
||||
|
||||
if (true === $includeDiscount) {
|
||||
$total -= $this->getDiscount();
|
||||
$tax -= $this->getDiscount() - Calculator::getUntaxedOrderDiscount($this);
|
||||
|
||||
if ($total < 0) {
|
||||
$total = 0;
|
||||
}
|
||||
|
||||
if ($tax < 0) {
|
||||
$tax = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (false !== $includePostage) {
|
||||
$total += (float)$this->getPostage();
|
||||
$tax += (float)$this->getPostageTax();
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is thge legacy way of computing this order amount with taxes. The tax amount is returned in the $tax parameter.
|
||||
*
|
||||
* The order amount is only available once the order is persisted in database.
|
||||
* During invoice process, use all cart methods instead of order methods (the order doest not exists at this moment)
|
||||
*
|
||||
* @param float|int $tax (output only) returns the tax amount for this order
|
||||
* @param bool $includePostage if true, the postage cost is included to the total
|
||||
* @param bool $includeDiscount if true, the discount will be included to the total
|
||||
* @return float
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function getTotalAmountLegacy(&$tax = 0, $includePostage = true, $includeDiscount = true)
|
||||
{
|
||||
$amount = (float)OrderProductQuery::create()
|
||||
->filterByOrderId($this->getId())
|
||||
->withColumn('SUM(
|
||||
' . OrderProductTableMap::COL_QUANTITY . '
|
||||
* IF(' . OrderProductTableMap::COL_WAS_IN_PROMO . ' = 1, ' . OrderProductTableMap::COL_PROMO_PRICE . ', ' . OrderProductTableMap::COL_PRICE . ')
|
||||
)', 'total_amount')
|
||||
->select(['total_amount'])
|
||||
->findOne();
|
||||
|
||||
$tax = (float)OrderProductTaxQuery::create()
|
||||
->useOrderProductQuery()
|
||||
->filterByOrderId($this->getId())
|
||||
->endUse()
|
||||
->withColumn('SUM(
|
||||
' . OrderProductTableMap::COL_QUANTITY . '
|
||||
* IF(' . OrderProductTableMap::COL_WAS_IN_PROMO . ' = 1, ' . OrderProductTaxTableMap::COL_PROMO_AMOUNT . ', ' . OrderProductTaxTableMap::COL_AMOUNT . ')
|
||||
)', 'total_tax')
|
||||
->select(['total_tax'])
|
||||
->findOne();
|
||||
|
||||
$total = $amount + $tax;
|
||||
|
||||
// @todo : manage discount : free postage ?
|
||||
if (true === $includeDiscount) {
|
||||
$total -= $this->getDiscount();
|
||||
|
||||
if ($total < 0) {
|
||||
$total = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (false !== $includePostage) {
|
||||
$total += \floatval($this->getPostage());
|
||||
$tax += \floatval($this->getPostageTax());
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute this order weight.
|
||||
*
|
||||
* The order weight is only available once the order is persisted in database.
|
||||
* During invoice process, use all cart methods instead of order methods (the order doest not exists at this moment)
|
||||
*
|
||||
* @return float
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function getWeight()
|
||||
{
|
||||
$weight = 0;
|
||||
|
||||
/* browse all products */
|
||||
foreach ($this->getOrderProducts() as $orderProduct) {
|
||||
$weight += $orderProduct->getQuantity() * (double)$orderProduct->getWeight();
|
||||
}
|
||||
|
||||
return $weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the postage without tax
|
||||
* @return float|int
|
||||
*/
|
||||
public function getUntaxedPostage()
|
||||
{
|
||||
if (0 < $this->getPostageTax()) {
|
||||
$untaxedPostage = $this->getPostage() - $this->getPostageTax();
|
||||
} else {
|
||||
$untaxedPostage = $this->getPostage();
|
||||
}
|
||||
|
||||
return $untaxedPostage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current order contains at less 1 virtual product with a file to download
|
||||
*
|
||||
* @return bool true if this order have at less 1 file to download, false otherwise.
|
||||
*/
|
||||
public function hasVirtualProduct()
|
||||
{
|
||||
$virtualProductCount = OrderProductQuery::create()
|
||||
->filterByOrderId($this->getId())
|
||||
->filterByVirtual(1, Criteria::EQUAL)
|
||||
->count()
|
||||
;
|
||||
|
||||
return ($virtualProductCount !== 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the current order to NOT PAID
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function setNotPaid()
|
||||
{
|
||||
$this->setStatusHelper(OrderStatus::CODE_NOT_PAID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current status of this order is NOT PAID
|
||||
*
|
||||
* @param bool $exact if true, the status should be the exact required status, not a derived one.
|
||||
* @return bool true if this order is NOT PAID, false otherwise.
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function isNotPaid($exact = true)
|
||||
{
|
||||
return $this->getOrderStatus()->isNotPaid($exact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the current order to PAID
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function setPaid()
|
||||
{
|
||||
$this->setStatusHelper(OrderStatus::CODE_PAID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current status of this order is PAID
|
||||
*
|
||||
* @param bool $exact if true, the status should be the exact required status, not a derived one.
|
||||
* @return bool true if this order is PAID, false otherwise.
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function isPaid($exact = true)
|
||||
{
|
||||
return $this->getOrderStatus()->isPaid($exact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the current order to PROCESSING
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function setProcessing()
|
||||
{
|
||||
$this->setStatusHelper(OrderStatus::CODE_PROCESSING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current status of this order is PROCESSING
|
||||
*
|
||||
* @param bool $exact if true, the status should be the exact required status, not a derived one.
|
||||
* @return bool true if this order is PROCESSING, false otherwise.
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function isProcessing($exact = true)
|
||||
{
|
||||
return $this->getOrderStatus()->isProcessing($exact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the current order to SENT
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function setSent()
|
||||
{
|
||||
$this->setStatusHelper(OrderStatus::CODE_SENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current status of this order is SENT
|
||||
*
|
||||
* @param bool $exact if true, the status should be the exact required status, not a derived one.
|
||||
* @return bool true if this order is SENT, false otherwise.
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function isSent($exact = true)
|
||||
{
|
||||
return $this->getOrderStatus()->isSent($exact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the current order to CANCELED
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function setCancelled()
|
||||
{
|
||||
$this->setStatusHelper(OrderStatus::CODE_CANCELED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current status of this order is CANCELED
|
||||
*
|
||||
* @param bool $exact if true, the status should be the exact required status, not a derived one.
|
||||
* @return bool true if this order is CANCELED, false otherwise.
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function isCancelled($exact = true)
|
||||
{
|
||||
return $this->getOrderStatus()->isCancelled($exact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the current order to REFUNDED
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function setRefunded()
|
||||
{
|
||||
$this->setStatusHelper(OrderStatus::CODE_REFUNDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current status of this order is REFUNDED
|
||||
*
|
||||
* @param bool $exact if true, the status should be the exact required status, not a derived one.
|
||||
* @return bool true if this order is REFUNDED, false otherwise.
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function isRefunded($exact = true)
|
||||
{
|
||||
return $this->getOrderStatus()->isRefunded($exact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the current order to the provided status
|
||||
*
|
||||
* @param string $statusCode the status code, one of OrderStatus::CODE_xxx constants.
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function setStatusHelper($statusCode)
|
||||
{
|
||||
if (null !== $ordeStatus = OrderStatusQuery::create()->findOneByCode($statusCode)) {
|
||||
$this->setOrderStatus($ordeStatus)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the payment module
|
||||
*
|
||||
* @return \Thelia\Module\PaymentModuleInterface
|
||||
* @throws TheliaProcessException
|
||||
*/
|
||||
public function getPaymentModuleInstance()
|
||||
{
|
||||
if (null === $paymentModule = ModuleQuery::create()->findPk($this->getPaymentModuleId())) {
|
||||
throw new TheliaProcessException("Payment module ID=" . $this->getPaymentModuleId() . " was not found.");
|
||||
}
|
||||
|
||||
return $paymentModule->createInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the delivery module
|
||||
*
|
||||
* @return \Thelia\Module\BaseModuleInterface
|
||||
* @throws TheliaProcessException
|
||||
*/
|
||||
public function getDeliveryModuleInstance()
|
||||
{
|
||||
if (null === $deliveryModule = ModuleQuery::create()->findPk($this->getDeliveryModuleId())) {
|
||||
throw new TheliaProcessException("Delivery module ID=" . $this->getDeliveryModuleId() . " was not found.");
|
||||
}
|
||||
|
||||
return $deliveryModule->createInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if stock was decreased at stock creation for this order.
|
||||
* TODO : we definitely have to store modules in an order_modules table juste like order_product and other order related information.
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return bool true if the stock was decreased at order creation, false otherwise
|
||||
*/
|
||||
public function isStockManagedOnOrderCreation(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$paymentModule = $this->getPaymentModuleInstance();
|
||||
|
||||
$event = new ManageStockOnCreationEvent($paymentModule);
|
||||
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::getModuleEvent(
|
||||
TheliaEvents::MODULE_PAYMENT_MANAGE_STOCK,
|
||||
$paymentModule->getCode()
|
||||
),
|
||||
$event
|
||||
);
|
||||
|
||||
return (null !== $event->getManageStock())
|
||||
? $event->getManageStock()
|
||||
: $paymentModule->manageStockOnCreation();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user