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(); } }