<?php
namespace AWEcommerceBundle\Manager;
use AppBundle\Entity\Ecommerce\ProductVariantCustom;
use AppBundle\Entity\ItemCustom;
use AppBundle\Entity\Ecommerce\Order;
use AppBundle\Entity\Ecommerce\OrderItem;
use AppBundle\Entity\Ecommerce\ProductVariantCustom as ProductVariant;
use AppBundle\Entity\Ecommerce\Promotion;
use AWEcommerceBundle\Entity\Setting;
use AWEcommerceBundle\Entity\Shipping\ShippingMethodInterface;
use AWEcommerceBundle\Entity\Shipping\ShippingMethod;
use AWEcommerceBundle\Entity\User;
use AWCmsBundle\Entity\Site;
use AWCmsBundle\Manager\SiteManager;
use AWEcommerceBundle\Event\OrderEvent;
use AWEcommerceBundle\Model\AdjustmentInterface;
use AWEcommerceBundle\PromotionAction\FreeShippingPromotionActionCommand;
use AWEcommerceBundle\Repository\OrderRepository;
use AWEcommerceBundle\Service\Shipping\ShippingService;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Psr\Log\LoggerInterface;
use Sylius\Component\Order\Factory\AdjustmentFactory;
use Sylius\Component\Order\Model\Adjustment;
use Sylius\Component\Order\Model\OrderInterface;
use Sylius\Component\Order\Modifier\OrderItemQuantityModifier;
use Sylius\Component\Promotion\Action\PromotionApplicator;
use Sylius\Component\Promotion\Checker\Eligibility\CompositePromotionEligibilityChecker;
use Sylius\Component\Resource\Factory\Factory;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Translation\TranslatorInterface;
class OrderManager
{
/**
* @var ShippingService
*/
protected $shippingService;
/**
* @var PromotionApplicator
*/
protected $promotionApplicator;
/**
* @var ObjectManager
*/
protected $em;
/** @var string */
protected $orderEntity;
/**
* @var Factory
*/
protected $orderFactory;
/**
* @var AdjustmentFactory
*/
protected $adjustmentFactory;
/**
* @var SettingManager
*/
protected $settingManager;
/**
* @var SiteManager
*/
protected $siteManager;
protected $eligibilityChecker;
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var FreeShippingPromotionActionCommand
*/
protected $freeShippingPromotionActionCommand;
protected $orderItemQuantityModifier;
protected $translator;
/** @var OrderItemManager */
protected $orderItemManager;
/**
* @var PromotionManager
*/
protected PromotionManager $promotionManager;
protected LoggerInterface $logger;
public function __construct(
EntityManagerInterface $em,
$orderEntity,
ShippingService $shippingService,
PromotionApplicator $promotionApplicator,
Factory $orderFactory,
SettingManager $settingManager,
SiteManager $siteManager,
AdjustmentFactory $adjustmentFactory,
CompositePromotionEligibilityChecker $eligibilityChecker,
FreeShippingPromotionActionCommand $freeShippingPromotionActionCommand,
EventDispatcherInterface $eventDispatcher,
OrderItemQuantityModifier $orderItemQuantityModifier,
OrderItemManager $orderItemManager,
PromotionManager $promotionManager,
TranslatorInterface $translator,
LoggerInterface $logger
){
$this->em = $em;
$this->orderEntity = $orderEntity;
$this->shippingService = $shippingService;
$this->promotionApplicator = $promotionApplicator;
$this->orderFactory = $orderFactory;
$this->settingManager = $settingManager;
$this->siteManager = $siteManager;
$this->adjustmentFactory = $adjustmentFactory;
$this->eligibilityChecker = $eligibilityChecker;
$this->freeShippingPromotionActionCommand = $freeShippingPromotionActionCommand;
$this->eventDispatcher = $eventDispatcher;
$this->orderItemQuantityModifier = $orderItemQuantityModifier;
$this->translator = $translator;
$this->orderItemManager = $orderItemManager;
$this->promotionManager = $promotionManager;
$this->logger = $logger;
}
/**
* @return OrderRepository|\Doctrine\ORM\EntityRepository
*/
public function getRepository(){
return $this->em->getRepository($this->orderEntity);
}
/**
* @param User $user
* @return Order|object
*/
public function getCartOrder(?User $user)
{
return $this->getRepository()->findOneBy([
'user' => $user,
'state' => OrderInterface::STATE_CART,
'site' => $this->siteManager->getSite()
]);
}
/**
* @param User|\AppBundle\Entity\User $user
* @param bool $save
* @return Order|mixed|object
* @throws \Exception
*/
public function getOrCreateCartOrder(User $user, $save = true){
/** @var Order $order */
$order = $this->getCartOrder($user);
$site = $this->siteManager->getSite();
if(empty($order)) {
$order = $this->orderFactory->createNew();
$order->setUser($user);
$setting = $this->settingManager->findBySite($site);
$order->setTaxPercentage($setting->getTaxes());
$order->setSite($site);
if($save){
$this->save($order);
$order->generateNumber();
$this->save($order);
}
}
return $order;
}
/**
* @param User $user
* @param ProductVariant $productVariant
* @param int $quantity
* @return OrderItem
* @throws \AWEcommerceBundle\Exception\OutOfStockException
*/
public function addProductVariantToUser(User $user, ProductVariant $productVariant, $quantity = 1, $customText = null)
{
/** @var Order $order */
$order = $this->getOrCreateCartOrder($user);
$this->eventDispatcher->dispatch(new OrderEvent($order), OrderEvent::POST_CREATED_EVENT);
$this->eventDispatcher->dispatch( new OrderEvent($order), OrderEvent::PRE_UPDATED_EVENT);
$this->orderItemManager->createOrderItem($order, $productVariant, $quantity, $customText);
$this->eventDispatcher->dispatch(new OrderEvent($order), OrderEvent::POST_UPDATED_EVENT);
return $order;
}
public function updateOrderPackagingWeight(Order $order, $flush = true)
{
$site = $order->getSite();
$setting = $this->settingManager->findBySite($site);
$weight = 0;
/** @var OrderItem $item */
foreach ($order->getItems() as $item){
if($weight < $item->getProduct()->getPackagingWeight()){
$weight = $item->getProduct()->getPackagingWeight();
}
}
$weight = ($weight === 0 and $setting) ? $setting->getDefaultPackagingWeight() : $weight;
$order->setPackagingWeight($weight);
if($flush) {
$this->save($order);
}
}
public function getOrdersByState(User $user, $state)
{
return $this->getRepository()->findBy(['user' => $user, 'state' => $state]);
}
public function save(Order $order){
$this->em->persist($order);
$this->em->flush();
}
public function delete(Order $order){
$this->em->remove($order);
$this->em->flush();
}
/**
* @param User $oldUser
* @param User|UserInterface $newUser
*/
public function moveOrderToCustomer(User $oldUser, User $newUser)
{
$oldUserCart = $this->getCartOrder($oldUser);
$newUserCart = $this->getCartOrder($newUser);
if($newUserCart)
$this->eventDispatcher->dispatch(OrderEvent::PRE_UPDATED_EVENT, new OrderEvent($newUserCart));
if($oldUserCart and $newUserCart){
/** @var OrderItem $oldItem */
foreach ($oldUserCart->getItems() as $oldItem) {
$added = false;
if(!$oldItem->getProduct()->isEditable()){
/** @var OrderItem $newItem */
foreach ($newUserCart->getItems() as $newItem) {
if($newItem->getVariant()->getId() === $oldItem->getVariant()->getId()){
$quantity = $newItem->getQuantity() + $oldItem->getQuantity();
$this->orderItemQuantityModifier->modify($newItem, $quantity);
$added = true;
}
}
}
if(!$added){
$oldItem->setOrder($newUserCart);
$this->em->persist($oldItem);
}
}
$this->em->remove($oldUserCart);
$this->em->flush();
}
if($newUserCart)
$this->eventDispatcher->dispatch(OrderEvent::POST_UPDATED_EVENT, new OrderEvent($newUserCart));
/** @var Order[] $orders */
$orders = $this->getRepository()->findBy(['user' => $oldUser]);
foreach ($orders as $order) {
$order->setUser($newUser);
$this->save($order);
}
}
/**
* @param Order $order
* @param bool $flush
*/
public function setDefaultShipping(Order $order, $flush = true)
{
if ($order->getShippingMethod() == null OR ($order->getShippingMethod() != null AND
($order->getShippingMethod()->getMaxWeight() < $order->getTotalWeight() OR $order->getShippingMethod()->getMinWeight() > $order->getTotalWeight()))) {
$shippingMethod = $this->shippingService->findFirstShipping($order->getTotalWeight(), $order->getUser()->getLocationCode());
if($shippingMethod){
$order->setShippingMethod($shippingMethod);
}else{
$order->setShippingMethod(null);
}
if($flush) {
$this->save($order);
}
}
}
public function reCalculate(Order $order, $flush = true): void
{
$this->setDefaultShipping($order, $flush);
if($order->getShippingMethod()){
$countryCodeAlpha2 = ($order->getShippingAddress() and $order->getShippingAddress()->getCountryCode())
? $order->getShippingAddress()->getCountryCode()
: $order->getUser()->getLocationCode();
$amount = ceil($order->getShippingMethod()->getPriceByCountryCodeAlpha2($countryCodeAlpha2)) * 100;
/** @var Adjustment|null $shippingAdjustment */
$shippingAdjustment = $order->getAdjustments(AdjustmentInterface::SHIPPING_ADJUSTMENT)->first();
if(!$shippingAdjustment OR $amount != $shippingAdjustment->getAmount()){
$order->removeShippingAdjustment();
$adjustment = $this->adjustmentFactory->createNew();
$adjustment->setType(AdjustmentInterface::SHIPPING_ADJUSTMENT);
$adjustment->setAmount(ceil($order->getShippingMethod()->getPriceByCountryCodeAlpha2($countryCodeAlpha2)) * 100);
$adjustment->setNeutral(false);
$adjustment->setLabel('Shipping - '.$order->getShippingMethod()->getName());
$order->addAdjustment($adjustment);
}
}
$order->recalculateAdjustmentsTotal();
$order->calculateTotalItems();
$order->recalculateItemsTotal();
}
/**
* @param Order $order
* @return bool
*/
public function generateBillingNumber(Order $order){
// FORMAT : F16-01-0001 -> FYY-MM-0001
if($order->getBillingNumber() != null OR $order->getState() != OrderInterface::STATE_CART){
return false;
}
/** @var Order $lastOrder */
$lastOrder = $this->getRepository()->findLatest(1);
$lastOrder = (!empty($lastOrder)) ? $lastOrder[0] : null;
if ($lastOrder AND $lastOrder->getCheckoutCompletedAt() AND $lastOrder->getCheckoutCompletedAt()->format('y-m') == date('y-m')) {
$num = (int) substr($lastOrder->getBillingNumber(), -4);
$num++;
} else {
$num = 1;
}
$num = sprintf("%04d", $num);
$order->setBillingNumber('F' . date('y-m') . '-' . $num);
$order->setCheckoutCompletedAt(new \DateTime());
return true;
}
/**
* @param Order $order
*/
public function newOrder(Order $order)
{
$this->generateBillingNumber($order);
if($order->getUser()->getBillingAddress()){
$billingAddress = clone $order->getUser()->getBillingAddress();
}
elseif($order->getUser()->getShippingAddress()){
$billingAddress = clone $order->getUser()->getShippingAddress();
}
else {
throw new \Exception("The user ".$order->getUser()->getEmail().' #'.$order->getUser()->getId().' don\'t have any address to finish the order !');
}
$shippingAddress = clone $order->getUser()->getShippingAddress();
$this->updateState($order, Order::STATE_NEW);
$order->setBillingAddress($billingAddress);
$order->setShippingAddress($shippingAddress);
/** @var Promotion $promotion */
$promotion = $order->getPromotion();
if($promotion){
$promotion->setUsed($promotion->getUsed() + 1);
$this->em->persist($promotion);
$this->em->flush();
}
$this->save($order);
}
public function updateState(Order $order, $state){
$order->setState($state);
$this->eventDispatcher->dispatch(new OrderEvent($order), OrderEvent::STATE_CHANGED_EVENT);
$this->em->persist($order);
$this->em->flush();
}
public function updateShippingMethodFreeShipping(Order $order, ShippingMethodInterface $shippingMethod)
{
if($shippingMethod->isEligibleFreeShipping() && $order->getTotalItems() >= $this->freeShippingPromotionActionCommand->getAmountForFreeShipping()){
$shippingMethod->setIsEligibleFreeShipping(true);
}else{
$shippingMethod->setIsEligibleFreeShipping(false);
}
}
/**
* @param Order $order
*/
protected function removeOrderWalletAdjustment(Order $order): void
{
foreach ($order->getAdjustments() as $adjustment) {
if($adjustment->getType() === AdjustmentInterface::WALLET_ADJUSTMENT){
$adjustmentAmount = $adjustment->getAmount();
$order->getUser()->setWallet($order->getUser()->getWallet() + abs($adjustmentAmount));
$order->removeAdjustment($adjustment);
}
}
}
public function triggerUpdateEvent(ConsoleOutput $output = null){
/** @var Order[] $orders */
$orders = $this->getRepository()->findBy(['state' => OrderInterface::STATE_CART]);
if($output){
$progressBar = new ProgressBar($output, count($orders));
}
$count = 0;
foreach ($orders as $order){
if($order->getId() == 2281){
$this->eventDispatcher->dispatch(OrderEvent::PRE_UPDATED_EVENT, new OrderEvent($order));
$this->eventDispatcher->dispatch(OrderEvent::POST_UPDATED_EVENT, new OrderEvent($order));
}
if(isset($progressBar))
$progressBar->advance();
//
// $shippingMethod = $this->shippingService->findFirstShipping($order->getTotalWeight(), $order->getUser()->getLocationCode());
// if($shippingMethod){
// $order->setShippingMethod($shippingMethod);
// }
// $this->em->persist($order);
//
// if($count % 50 === 0){
// $this->em->flush();
// }
// $count++;
//
// if(isset($progressBar))
// $progressBar->advance();
}
}
public function getOptionState(){
$stateOptions[''] = $this->translator->trans("front.account.orders.states.all");
foreach ($this->orderEntity::EDITABLE_STATES as $STATE){
$nb = $this->getRepository()->countOrdersByStateAndSite($STATE, $this->siteManager->getSite());
$stateOptions[$STATE] = $this->translator->trans("front.account.orders.states.$STATE")." ($nb)";
}
return $stateOptions;
}
}