<?php
namespace Customize\EventListener;
use Customize\Controller\Trait\ConstanceTrait;
use Customize\Controller\Trait\KrakiFileTrait;
use Customize\Service\Cache\UserDataCache;
use Eccube\Entity\Order;
use Eccube\Entity\TaxRule;
use Eccube\Entity\Master\OrderItemType;
use Customize\Common\Utils;
use Eccube\Event\EventArgs;
use Eccube\Entity\OrderItem;
use Eccube\Event\EccubeEvents;
use Customize\Common\Constants;
use Eccube\Common\EccubeConfig;
use Eccube\Entity\ProductClass;
use Eccube\Event\TemplateEvent;
use Eccube\Service\CartService;
use Symfony\Component\Yaml\Parser;
use Customize\Entity\ProductUseDays;
use Doctrine\ORM\EntityManagerInterface;
use Eccube\Service\PurchaseFlow\PurchaseFlow;
use Symfony\Component\Security\Core\Security;
use Customize\Entity\OrderDetailAdditionalInfo;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Customize\Entity\GrayoutManagement;
use Carbon\Carbon;
class AkiCustomizerListener implements EventSubscriberInterface
{
use ConstanceTrait;
use KrakiFileTrait;
/**
* entityManagerInterface
*
* @var EntityManagerInterface
*/
private $entityManagerInterface;
/**
* session
*
* @var SessionInterface
*/
private $session;
/**
* kernelInterface
*
* @var KernelInterface
*/
private $kernelInterface;
/**
* eccubeConfig
*
* @var EccubeConfig
*/
private $eccubeConfig;
/**
* cartService
*
* @var CartService
*/
private $cartService;
private $purchaseFlow;
private $security;
private $securePackProductClass;
private $requestStack;
public function __construct(
EntityManagerInterface $entityManagerInterface,
SessionInterface $session,
KernelInterface $kernelInterface,
EccubeConfig $eccubeConfig,
CartService $cartService,
PurchaseFlow $cartPurchaseFlow,
Security $security,
UserDataCache $userDataCache,
RequestStack $requestStack
) {
$this->entityManagerInterface = $entityManagerInterface;
$this->session = $session;
$this->kernelInterface = $kernelInterface;
$this->eccubeConfig = $eccubeConfig;
$this->cartService = $cartService;
$this->purchaseFlow = $cartPurchaseFlow;
$this->security = $security;
$this->requestStack = $requestStack;
$this->setUpValue();
$this->getSettingFile($this->eccubeConfig);
$this->securePackProductClass = $userDataCache->getSecurePackProductClass();
}
public function onRenderProductDetail(TemplateEvent $event)
{
$parameters = $event->getParameters();
$product = $parameters["Product"];
$request = $this->requestStack->getCurrentRequest();
$locale = $request->getLocale();
$productId = $product->getId();
// if product is foreign
if ($locale != Constants::LOCALE_JAPAN) {
$not_available_dates = [];
} else {
$productClassId = $product->getProductClasses()[0]->getId();
$beforeUseDays = $this->DEFAULT_BEFORE_USE_DAYS;
$afterUseDays = $this->DEFAULT_AFTER_USE_DAYS;
// show unavailable days
$useDays = $this->entityManagerInterface->getRepository(ProductUseDays::class)
->findOneBy(["product_id" => $productId]);
if ($useDays) {
$beforeUseDays = $useDays->getBeforeUseDays();
$afterUseDays = $useDays->getAfterUseDays();
}
$today = date("Y-m-d", strtotime("-10 days"));
$query = $this->entityManagerInterface->createQueryBuilder()
->select("odai")
->from(OrderDetailAdditionalInfo::class, "odai")
->innerJoin(Order::class, 'odr')
->where('odai.product_class_id = :product_class_id and odai.wear_date >= :wear_date')
->andWhere('odai.order_id = odr.id')
->andWhere('odr.OrderStatus <> 3')
->setParameter('product_class_id', $productClassId)
->setParameter('wear_date', $today)
->getQuery();
$orderDetailAdditionalInfoList = $query->getResult();
$not_available_dates = array();
foreach ($orderDetailAdditionalInfoList as $orderDetailAdditionalInfo) {
if (!empty($orderDetailAdditionalInfo->getBeforeUseDays())) {
$beforeUseDays = $orderDetailAdditionalInfo->getBeforeUseDays();
}
if (!empty($orderDetailAdditionalInfo->getAfterUseDays())) {
$afterUseDays = $orderDetailAdditionalInfo->getAfterUseDays();
}
$wearDate = $orderDetailAdditionalInfo->getWearDate()->format('Y-m-d');
$not_available_dates[] = $wearDate;
for ($i = $beforeUseDays; $i > 0; $i--) {
$tmpDate = strtotime($wearDate . " - {$i} day");
$not_available_dates[] = date("Y-m-d", $tmpDate);
}
for ($i = 1; $i <= $afterUseDays; $i++) {
$tmpDate = strtotime($wearDate . " + {$i} day");
$not_available_dates[] = date("Y-m-d", $tmpDate);
}
}
// Custom grayout manage
$grayoutDays = $this->entityManagerInterface->getRepository(GrayoutManagement::class)
->findBy(["product_id" => $productId]);
if ($grayoutDays) {
$dates = [];
foreach ($grayoutDays as $grayoutDay) {
$startDate = Carbon::parse($grayoutDay->getStartDate());
$endDate = Carbon::parse($grayoutDay->getEndDate());
// When startdate is less than or equal, add 1 day
while ($startDate->lte($endDate)) {
$dates[] = $startDate->format('Y-m-d');
$startDate->addDay();
}
}
$not_available_dates = array_merge($not_available_dates, $dates);
$not_available_dates = array_unique($not_available_dates);
sort($not_available_dates);
}
}
$parameters += ["not_available_dates" => $not_available_dates];
$time = time();
$today = date("d", $time);
$year = date("Y", $time);
$month = date("m", $time);
$end_of_month = date("t", $time);
$start_week = date('w', strtotime("$year/$month/1"));
$next_month_time = strtotime("$year/$month/1 + 1 month");
$next_month_year = date("Y", $next_month_time);
$next_month = date("m", $next_month_time);
$next_end_of_month = date("t", $next_month_time);
$next_month_start_week = date('w', $next_month_time);
$parameters += ["year" => intval($year)];
$parameters += ["current_month" => intval($month)];
$parameters += ["today" => intval($today)];
$parameters += ["current_end_of_month" => intval($end_of_month)];
$parameters += ["start_week" => intval($start_week)];
$parameters += ["next_month_year" => intval($next_month_year)];
$parameters += ["next_month" => intval($next_month)];
$parameters += ["next_end_of_month" => intval($next_end_of_month)];
$parameters += ["next_month_start_week" => intval($next_month_start_week)];
$parameters += ["product_id" => $productId];
$parameters += ["order_type" => ""];
if ($this->cartService->getCart()) {
$cartItems = $this->cartService->getCart()->getItems();
foreach ($cartItems as $cartItem) {
if ($cartItem->getOrderType()) {
$parameters["order_type"] = $cartItem->getOrderType();
break;
}
}
}
/*
If you come from a date search, set the search date as the default for ``
select date of use'' and ``desired store to visit for dressing''. (Date can be changed)
*/
$searchDate = "";
if ($this->session->has(Constants::SEARCH_DATE)) {
$searchDate = $this->session->get(Constants::SEARCH_DATE);
}
$parameters += [Constants::SEARCH_DATE => $searchDate];
$settings = $this->settings;
$form_settings = [
"order_type" => Utils::getOrderType($settings),
"purpose" => Utils::getPurpose($settings),
"foot_size" => Utils::getFootSize($settings),
"body_height" => Utils::getBodyHeight($settings),
"body_type" => Utils::getBodyType($settings),
"hair_make" => Utils::getHairMake($settings),
"time_departure" => Utils::getTimeDeparture($settings),
"visit_store" => Utils::getVisitStore($settings),
"secure_pack" => Utils::getSecurePack($settings),
"photo_plan" => Utils::getPhotoPlan($settings),
"decade" => Utils::getDecade($settings),
"number_of_guests" => Utils::getNumberOfGuests($settings),
"gender" => Utils::getGender($settings)
];
$parameters += [Constants::FORM_SETTINGS => $form_settings];
$event->setParameters($parameters);
}
public function onRenderCartIndex(TemplateEvent $event)
{
$parameters = $event->getParameters();
$settings = $this->settings;
$parameters += ["secure_pack_code" => $this->securePackProductClass->getCode()];
$parameters += ["secure_pack_price" => intval($settings["secure_pack"]["price"])];
$event->setParameters($parameters);
}
//getOrderTypeForSession in AKI old
private function getOrderTypeForCart()
{
$orderType = "";
if (!empty($this->cartService->getCart())) {
$itemList = $this->cartService->getCart()->getItems();
foreach ($itemList as $item) {
//order type of cart is the order type of the first kimono product in cart
if ($item->getOrderType()) {
$orderType = $item->getOrderType();
break;
}
}
}
return $orderType;
}
private function getActualPrice()
{
$cart = $this->cartService->getCart();
$actual_price = 0;
if (is_null($cart))
return $actual_price;
foreach ($cart->getCartItems() as $cartItem) {
$productClass = $cartItem->getProductClass();
$actual_price += $productClass->getPrice02();
}
return $actual_price;
}
//onFrontProductDetailInitialize in AKI_D-old
public function onFrontProductDetailInitialize(EventArgs $event)
{
$locale = $event->getRequest()->get('_locale') ?? CONSTANTS::LOCALE_JAPAN;
$isJapanese = $locale === Constants::LOCALE_JAPAN;
/** custom01 */
$builder = $event->getArgument('builder');
$settings = $this->settings;
// 来店着付け OR 宅配レンタルの隠し項目
$builder->add(
Constants::PLG_ORDER_TYPE_NAME,
HiddenType::class,
[
'required' => true,
'label' => false,
'mapped' => false,
'attr' => array('value' => 'visit'),
'constraints' => array(
new Assert\NotBlank()
)
]
);
/** custom03 フォーム再構築 */
// 着用日
$builder->add(
Constants::PLG_WEAR_DATE_NAME,
TextType::class,
array(
'required' => true,
'label' => "着用日を選択して下さい",
'mapped' => false,
'constraints' => array(
new Assert\NotBlank(),
new Assert\Date()
)
)
);
// 用途
if ($locale === Constants::LOCALE_JAPAN) {
$builder->add(
Constants::PLG_PURPOSE_NAME,
ChoiceType::class,
array(
'required' => true,
'label' => "用途:",
'mapped' => false,
'choices' => Utils::getPurpose($settings),
'constraints' => array(
new Assert\NotBlank()
)
)
);
}
if (!$isJapanese) {
$builder->add(
Constants::PLG_NUMBER_OF_GUESTS,
ChoiceType::class,
[
'required' => false,
'label' => "number_of_guests",
'mapped' => false,
'choices' => Utils::getNumberOfGuests($settings),
]
);
$builder->add(
Constants::PLG_GENDER,
ChoiceType::class,
[
'required' => true,
'label' => "Gender",
'mapped' => false,
'choices' => Utils::getGender($settings),
'constraints' => new Assert\Callback([$this, 'validateEmptyOnNumberOfGuests'])
]
);
}
// 身長
$builder->add(
Constants::PLG_BODY_HEIGHT_NAME,
ChoiceType::class,
array(
'required' => true,
'label' => "身長:",
'mapped' => false,
'choices' => Utils::getBodyHeight($settings),
'constraints' => $isJapanese ? [new Assert\NotBlank()] : [new Assert\Callback([$this, 'validateEmptyOnNumberOfGuests'])]
)
);
// 足のサイズ
$builder->add(
Constants::PLG_FOOT_SIZE_NAME,
ChoiceType::class,
array(
'required' => true,
'label' => "足のサイズ:",
'mapped' => false,
'choices' => Utils::getFootSize($settings),
'constraints' => $isJapanese ? [new Assert\NotBlank()] : [new Assert\Callback([$this, 'validateEmptyOnNumberOfGuests'])]
)
);
if ($locale === Constants::LOCALE_JAPAN) {
// 年代
$builder->add(
Constants::PLG_DECADE,
ChoiceType::class,
array(
'required' => true,
'label' => "年代:",
'mapped' => false,
'choices' => Utils::getDecade($settings),
'constraints' => array(
new Assert\NotBlank()
)
)
);
}
if ($locale != Constants::LOCALE_JAPAN) {
$builder->add(
Constants::PLG_REMARK,
TextareaType::class,
[
'required' => false,
'label' => 'Remark',
'mapped' => false,
'attr' => [
'maxlength' => 3000,
'rows' => 5,
],
]
);
}
// 体型
$builder->add(
Constants::PLG_BODY_TYPE_NAME,
ChoiceType::class,
array(
'required' => false,
'label' => "体型:",
'mapped' => false,
'choices' => Utils::getBodyType($settings),
'expanded' => true,
'multiple' => true
)
);
// 安心パック
$builder->add(
Constants::PLG_SECURE_PACK_NAME,
ChoiceType::class,
array(
'required' => false,
'label' => "安心パック",
'mapped' => false,
'choices' => Utils::getSecurePack($settings),
'expanded' => true,
'multiple' => true
)
);
/**
* 来店着付けの場合フォームの項目追加
*/
if ("visit" == $event->getRequest()->request->get("plg_order_type")) {
// フォト
$builder->add(
Constants::PLG_NEED_PHOTO,
ChoiceType::class,
array(
'required' => false,
'label' => "フォト",
'mapped' => false,
'choices' => Utils::getPhotoPlan($settings)
)
);
// ヘアメイク有り無し
$builder->add(
Constants::PLG_NEED_HAIR_MAKE,
ChoiceType::class,
array(
'required' => true,
'label' => "ヘア・メイクのご希望の有無",
'mapped' => false,
'choices' => Utils::getHairMake($settings),
'constraints' => array(
new Assert\NotBlank()
)
)
);
// 来店希望店舗
$builder->add(
Constants::PLG_VISIT_STORE_NAME,
ChoiceType::class,
array(
'required' => false,
'label' => "着付けご来店希望店舗",
'mapped' => false,
'choices' => Utils::getVisitStore($settings)
)
);
// ご出発予定時間
$builder->add(
Constants::PLG_TIME_DEPARTURE_NAME,
ChoiceType::class,
array(
'required' => true,
'label' => "ご出発予定時間",
'mapped' => false,
'choices' => Utils::getTimeDeparture($settings),
'constraints' => array(
new Assert\NotBlank(),
// new Assert\Time()
)
)
);
}
}
public function validateEmptyOnNumberOfGuests($value, ExecutionContextInterface $context)
{
$form = $context->getRoot();
$numberOfGuest = $form->get(Constants::PLG_NUMBER_OF_GUESTS)->getData();
if ($numberOfGuest == 1 && empty($value)) {
$context->buildViolation('This field cannot be empty if number of guests is 1.')
->addViolation();
}
}
//onFrontProductDetailComplete in AKI_D-old
public function onFrontProductAddCartComplete(EventArgs $eventArgs)
{
$form = $eventArgs->getArgument("form");
$data = [];
foreach ($form->all() as $field => $valueForm) {
$data[$field] = $valueForm->getData();
}
// FIXME 安心パック
$settings = $this->settings;
if (!empty($data[Constants::PLG_SECURE_PACK_NAME])) {
//create secure_pack item
$this->cartService->addProductOverride($this->securePackProductClass->getId(), 1);
$Carts = $this->cartService->getCarts();
foreach ($Carts as $Cart) {
$result = $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart, $this->security->getUser()));
// 復旧不可のエラーが発生した場合は追加した明細を削除.
if ($result->hasError()) {
$this->cartService->removeProduct($this->securePackProductClass->getId());
foreach ($result->getErrors() as $error) {
$errorMessages[] = $error->getMessage();
}
}
foreach ($result->getWarning() as $warning) {
$errorMessages[] = $warning->getMessage();
}
}
$this->cartService->save();
}
}
public function onRenderShoppingIndex(TemplateEvent $event)
{
$parameters = $event->getParameters();
$orderType = "";
$orderDetailAdditionalInfoList = $this->getVariantListFromCart();
if (!empty($orderDetailAdditionalInfoList)) {
$orderType = $orderDetailAdditionalInfoList[0]->getOrderType();
}
$parameters += ['orderType' => $orderType];
$parameters += ['OrderDetailAdditionalInfoList' => $orderDetailAdditionalInfoList];
$parameters += ["secure_pack_code" => $this->securePackProductClass->getCode()];
$parameters += ["secure_pack_price" => intval($this->settings["secure_pack"]["price"])];
$form_settings = ["pay_method" => Utils::getPayMethod($this->settings)];
$parameters += [Constants::FORM_SETTINGS => $form_settings];
$event->setParameters($parameters);
}
// Before checkout. add ordertype and additional info to the mailtemplate
public function onControllerShoppingCheckoutBefore(EventArgs $event)
{
$cart = $this->cartService->getCart();
$preOrderId = $cart["pre_order_id"];
$Order = $this->entityManagerInterface->getRepository(Order::class)->findOneBy(
[
'pre_order_id' => $preOrderId,
'OrderStatus' => $this->eccubeConfig['order_processing'],
]
);
if (!$Order) {
return;
}
$this->session->set("KrAkiCustomizer.orderId", $Order["id"]);
$orderDetailAdditionalInfoList = $this->getVariantListFromCart();
//Add orderType parameter to mail template
$orderType = "";
if (!empty($orderDetailAdditionalInfoList)) {
$orderType = $orderDetailAdditionalInfoList[0]->getOrderType();
}
$Order->orderDetailInfo = $orderDetailAdditionalInfoList;
$Order->orderType = $orderType;
$this->entityManagerInterface->persist($Order);
$this->entityManagerInterface->flush();
$this->entityManagerInterface->getConnection()->commit();
}
public function onFrontShoppingCompleteInitialize(EventArgs $event)
{
$this->entityManagerInterface->getConnection()->beginTransaction();
$orderDetailAdditionalInfoList = $this->getVariantListFromCart();
$session = $this->session;
if (empty($orderDetailAdditionalInfoList)) {
return;
}
$orderId = $session->get("KrAkiCustomizer.orderId");
$order = $this->entityManagerInterface->getRepository(Order::class)->find($orderId);
$orderDetails = [];
foreach ($order->getShippings() as $shipping) {
foreach ($shipping->getProductOrderItems() as $item) {
$orderDetails[] = $item;
}
}
foreach ($orderDetailAdditionalInfoList as $orderDetailAdditionalInfo) {
foreach ($orderDetails as $orderDetail) {
if ($this->isMatchingDetail($orderDetail, $orderDetailAdditionalInfo)) {
$orderDetailAdditional = $this->createOrderDetailAdditional($order, $orderDetail, $orderDetailAdditionalInfo);
$this->entityManagerInterface->persist($orderDetailAdditional);
break;
}
}
}
$this->entityManagerInterface->flush();
$this->entityManagerInterface->getConnection()->commit();
$this->cartService->clear();
}
private function isMatchingDetail($orderDetail, $orderDetailAdditionalInfo)
{
return $orderDetail->getProductClass()->getId() == $orderDetailAdditionalInfo->getProductClass()->getId();
}
private function createOrderDetailAdditional($order, $orderDetail, $orderDetailAdditionalInfo)
{
$request = $this->requestStack->getCurrentRequest();
$isJapanese = $request->getLocale() == Constants::LOCALE_JAPAN ? true : false;
$orderDetailAdditional = new OrderDetailAdditionalInfo;
$orderDetailAdditional->setOrderDetailId($orderDetail->getId());
$orderDetailAdditional->setOrderId($order->getId());
$orderDetailAdditional->setProductClassId(intval($orderDetailAdditionalInfo->getProductClass()->getId()));
$bodyType = $orderDetailAdditionalInfo->getBodyType();
if (!empty($bodyType)) {
$orderDetailAdditional->setBodyType(($bodyType));
} else {
$orderDetailAdditional->setBodyType(null);
}
$secure_pack = $orderDetailAdditionalInfo->getSecurePack();
if (!empty($secure_pack)) {
$orderDetailAdditional->setSecurePack($secure_pack[0]);
} else {
$orderDetailAdditional->setSecurePack(null);
}
$orderDetailAdditional->setActualPrice($orderDetail->getProductClass()->getPrice02());
$orderType = $orderDetailAdditionalInfo->getOrderType();
if ($orderType === 'fitting') {
$beforeUseDays = Constants::FITTING_BEFORE_USE_DAYS;
$afterUseDays = Constants::FITTING_AFTER_USE_DAYS;
} else {
$beforeUseDays = $this->DEFAULT_BEFORE_USE_DAYS;
$afterUseDays = $this->DEFAULT_AFTER_USE_DAYS;
}
if ($_SERVER['SERVER_NAME'] !== env("SALON_URL")) {
$productId = $orderDetail->getProduct()->getId();
$useDays = $this->entityManagerInterface->getRepository(ProductUseDays::class)->findOneBy([
"product_id" => $productId
]);
if ($useDays) {
$beforeUseDays = $useDays['before_use_days'];
$afterUseDays = $useDays['after_use_days'];
}
}
if ($isJapanese) {
$orderDetailAdditional->setBeforeUseDays($beforeUseDays);
$orderDetailAdditional->setAfterUseDays($afterUseDays);
}
//-------------------------------------------------------------
$orderDetailAdditional->setOrderType($orderDetailAdditionalInfo->getOrderType());
$orderDetailAdditional->setWearDate($orderDetailAdditionalInfo->getWearDate());
$orderDetailAdditional->setPurpose($orderDetailAdditionalInfo->getPurpose());
$orderDetailAdditional->setBodyHeight($orderDetailAdditionalInfo->getBodyHeight());
$orderDetailAdditional->setFootSize($orderDetailAdditionalInfo->getFootSize());
$orderDetailAdditional->setNeedPhoto($orderDetailAdditionalInfo->getNeedPhoto());
$orderDetailAdditional->setNeedHairMake($orderDetailAdditionalInfo->getNeedHairMake());
// $orderDetailAdditional->setDateVisit(); => date_visit alway null
$orderDetailAdditional->setTimeDeparture($orderDetailAdditionalInfo->getTimeDeparture());
$orderDetailAdditional->setVisitStore($orderDetailAdditionalInfo->getVisitStore());
$orderDetailAdditional->setDecade($orderDetailAdditionalInfo->getDecade());
$orderDetailAdditional->setRemark($orderDetailAdditionalInfo->getRemark());
$orderDetailAdditional->setGender($orderDetailAdditionalInfo->getGender());
if (!$isJapanese) {
$orderDetailAdditional->setNumberOfGuests($orderDetailAdditionalInfo->getNumberOfGuests());
}
return $orderDetailAdditional;
}
public function getVariantListFromCart()
{
$orderDetailAddtionalList = [];
if ($this->cartService->getCart()) {
foreach ($this->cartService->getCart()->getItems() as $cartItem) {
// if cart item is not secure_pack
if ($cartItem->getProductClass()->getId() != $this->securePackProductClass->getId()) {
$orderDetailAddtionalList[] = $cartItem;
}
}
}
return $orderDetailAddtionalList;
}
public static function getSubscribedEvents()
{
return [
EccubeEvents::FRONT_PRODUCT_DETAIL_INITIALIZE => 'onFrontProductDetailInitialize',
EccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE => 'onFrontProductDetailInitialize',
EccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE => 'onFrontProductAddCartComplete',
'Product/detail.twig' => 'onRenderProductDetail',
'Cart/index.twig' => 'onRenderCartIndex',
'Shopping/index.twig' => 'onRenderShoppingIndex',
'Shopping/confirm.twig' => 'onRenderShoppingIndex',
'FRONT_SHOPPING_CHECKOUT_BEFORE' => 'onControllerShoppingCheckoutBefore',
'onProcessingWhenOrderSuccess' => 'onFrontShoppingCompleteInitialize'
];
}
}