src/CompanyGroupBundle/Controller/QuoteController.php line 88

Open in your IDE?
  1. <?php
  2. namespace CompanyGroupBundle\Controller;
  3. use ApplicationBundle\Controller\GenericController;
  4. use ApplicationBundle\Modules\Authentication\Constants\UserConstants;
  5. use CompanyGroupBundle\Entity\SubscriptionQuote;
  6. use CompanyGroupBundle\Modules\Api\Service\LegacySubscriptionBillingService;
  7. use CompanyGroupBundle\Modules\Api\Service\PricingService;
  8. use Symfony\Component\HttpFoundation\JsonResponse;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  11. /**
  12.  * Customer-facing quote controller.
  13.  * All views extend central_header.html.twig.
  14.  * Entity manager: company_group
  15.  */
  16. class QuoteController extends GenericController
  17. {
  18.     /**
  19.      * GET /quote/promo-lookup?code=XXX
  20.      */
  21.     public function PromoCodeLookupAction(Request $request)
  22.     {
  23.         $code trim($request->query->get('code'''));
  24.         if (!$code) {
  25.             return new JsonResponse(['success' => false'message' => 'No code provided']);
  26.         }
  27.         $em $this->getDoctrine()->getManager('company_group');
  28.         $promo $em->getRepository('CompanyGroupBundle\Entity\PromoCode')->findOneBy(['code' => $code]);
  29.         if (!$promo) {
  30.             return new JsonResponse(['success' => false'message' => 'Invalid promo code']);
  31.         }
  32.         $now time();
  33.         if ($promo->getExpiresAtTs() && $promo->getExpiresAtTs() < $now) {
  34.             return new JsonResponse(['success' => false'message' => 'This promo code has expired']);
  35.         }
  36.         if ($promo->getStartsAtTs() && $promo->getStartsAtTs() > $now) {
  37.             return new JsonResponse(['success' => false'message' => 'This promo code is not yet active']);
  38.         }
  39.         if ($promo->getMaxUseCount() && $promo->getUseCountBalance() !== null && $promo->getUseCountBalance() <= 0) {
  40.             return new JsonResponse(['success' => false'message' => 'This promo code has reached its usage limit']);
  41.         }
  42.         return new JsonResponse([
  43.             'success' => true,
  44.             'id' => $promo->getId(),
  45.             'code' => $promo->getCode(),
  46.             'promo_type' => (int)$promo->getPromoType(),
  47.             'promo_value' => (float)$promo->getPromoValue(),
  48.             'max_discount' => $promo->getMaxDiscountAmount() ? (float)$promo->getMaxDiscountAmount() : null,
  49.             'min_amount' => $promo->getMinAmountForApplication() ? (float)$promo->getMinAmountForApplication() : null,
  50.             'label' => (int)$promo->getPromoType() === 1
  51.                 'EUR ' number_format((float)$promo->getPromoValue(), 2) . ' off'
  52.                 : (float)$promo->getPromoValue() . '% off',
  53.         ]);
  54.     }
  55.     /**
  56.      * POST /quote/calculate-price
  57.      */
  58.     public function CalculatePriceAction(Request $request)
  59.     {
  60.         /** @var PricingService $pricing */
  61.         $pricing $this->get('app.pricing_service');
  62.         $normal max(0, (int)$request->request->get('normal_users'0));
  63.         $admin max(0, (int)$request->request->get('admin_users'0));
  64.         $ml max(0, (int)$request->request->get('ml_users'0));
  65.         $cycle in_array($request->request->get('billing_cycle'), ['monthly''yearly'], true)
  66.             ? $request->request->get('billing_cycle')
  67.             : 'monthly';
  68.         $planType in_array($request->request->get('plan_type'), ['team''enterprise'], true)
  69.             ? $request->request->get('plan_type')
  70.             : 'team';
  71.         return new JsonResponse($pricing->getPriceBreakdown($normal$admin$ml$cycle$planType));
  72.     }
  73.     /**
  74.      * GET /quote/request
  75.      * POST /quote/request
  76.      */
  77.     public function RequestQuoteAction(Request $request)
  78.     {
  79.         $session $request->getSession();
  80.         if ($request->isMethod('GET')) {
  81.             $prefill = [
  82.                 'company_name' => $session->get('company_name'''),
  83.                 'customer_email' => $session->get(UserConstants::USER_EMAIL''),
  84.                 'customer_name' => $session->get('userName'''),
  85.                 'customer_phone' => '',
  86.                 'plan_type' => $request->query->get('plan'SubscriptionQuote::PLAN_TEAM),
  87.                 'normal_users' => max(1, (int)$request->query->get('normal_users'1)),
  88.                 'admin_users' => max(0, (int)$request->query->get('admin_users'0)),
  89.                 'ml_users' => max(0, (int)$request->query->get('ml_users'0)),
  90.                 'billing_cycle' => $request->query->get('billing_cycle''monthly'),
  91.                 'payment_type' => $request->query->get('payment_type''automatic'),
  92.             ];
  93.             $pricing $this->get('app.pricing_service');
  94.             $breakdown $pricing->getPriceBreakdown(
  95.                 $prefill['normal_users'],
  96.                 $prefill['admin_users'],
  97.                 $prefill['ml_users'],
  98.                 $prefill['billing_cycle'],
  99.                 $prefill['plan_type']
  100.             );
  101.             return $this->render('@CompanyGroup/pages/quotes/request_quote.html.twig', [
  102.                 'prefill' => $prefill,
  103.                 'breakdown' => $breakdown,
  104.                 'page_title' => 'Request a Quote',
  105.             ]);
  106.         }
  107.         $post $request->request;
  108.         $service $this->get('app.quote_service');
  109.         $data = [
  110.             'plan_type' => $post->get('plan_type'SubscriptionQuote::PLAN_TEAM),
  111.             'normal_user_count' => max(0, (int)$post->get('normal_users'0)),
  112.             'admin_user_count' => max(0, (int)$post->get('admin_users'0)),
  113.             'ml_user_count' => max(0, (int)$post->get('ml_users'0)),
  114.             'billing_cycle' => $post->get('billing_cycle''monthly'),
  115.             'payment_type' => $post->get('payment_type''automatic'),
  116.             'customer_email' => trim($post->get('customer_email''')),
  117.             'customer_name' => trim($post->get('customer_name''')),
  118.             'customer_phone' => trim($post->get('customer_phone''')),
  119.             'company_name' => trim($post->get('company_name''')),
  120.             'customer_notes' => trim($post->get('customer_notes''')),
  121.             'company_address' => trim($post->get('company_address''')),
  122.             'country' => trim($post->get('country''')),
  123.             'app_id' => $session->get('appId'),
  124.             'promo_code_id' => $post->get('promo_code_id'null),
  125.         ];
  126.         if (empty($data['customer_email'])) {
  127.             $this->addFlash('error''Please provide your email address.');
  128.             return $this->redirectToRoute('quote_request');
  129.         }
  130.         $quote $service->createCustomerQuote($data);
  131.         return $this->redirectToRoute('quote_view_customer', ['token' => $quote->getQuoteToken()]);
  132.     }
  133.     /**
  134.      * GET /quote/{token}
  135.      */
  136.     public function ViewQuoteAction(Request $request$token)
  137.     {
  138.         $service $this->get('app.quote_service');
  139.         $quote $service->findByToken($token);
  140.         if (!$quote) {
  141.             throw $this->createNotFoundException('Quote not found.');
  142.         }
  143.         $history $service->getHistory((int)$quote->getId());
  144.         $pricing $this->get('app.pricing_service');
  145.         $breakdown null;
  146.         if ($quote->getNormalUserCount() !== null) {
  147.             $breakdown $pricing->getPriceBreakdown(
  148.                 (int)$quote->getNormalUserCount(),
  149.                 (int)$quote->getAdminUserCount(),
  150.                 (int)$quote->getMlUserCount(),
  151.                 $quote->getBillingCycle() ?: 'monthly',
  152.                 $quote->getPlanType()
  153.             );
  154.         }
  155.         return $this->render('@CompanyGroup/pages/quotes/customer_quote_view.html.twig', [
  156.             'quote' => $quote,
  157.             'history' => $history,
  158.             'breakdown' => $breakdown,
  159.             'page_title' => 'Your Quote - HoneyBee ERP',
  160.         ]);
  161.     }
  162.     /**
  163.      * GET /quote/{token}/print
  164.      */
  165.     public function PrintQuoteAction(Request $request$token)
  166.     {
  167.         $service $this->get('app.quote_service');
  168.         $quote $service->findByToken($token);
  169.         if (!$quote) {
  170.             throw $this->createNotFoundException('Quote not found.');
  171.         }
  172.         $pricing $this->get('app.pricing_service');
  173.         $breakdown null;
  174.         if ($quote->getNormalUserCount() !== null) {
  175.             $breakdown $pricing->getPriceBreakdown(
  176.                 (int)$quote->getNormalUserCount(),
  177.                 (int)$quote->getAdminUserCount(),
  178.                 (int)$quote->getMlUserCount(),
  179.                 $quote->getBillingCycle() ?: 'monthly',
  180.                 $quote->getPlanType()
  181.             );
  182.         }
  183.         return $this->render('@CompanyGroup/pages/quotes/quote_print.html.twig', [
  184.             'quote' => $quote,
  185.             'breakdown' => $breakdown,
  186.         ]);
  187.     }
  188.     /**
  189.      * POST /quote/{token}/accept
  190.      */
  191.     public function AcceptQuoteAction(Request $request$token)
  192.     {
  193.         $service $this->get('app.quote_service');
  194.         $quote $service->findByToken($token);
  195.         if (!$quote) {
  196.             throw $this->createNotFoundException('Quote not found.');
  197.         }
  198.         $allowedStatuses = [
  199.             SubscriptionQuote::STATUS_SENT,
  200.             SubscriptionQuote::STATUS_MODIFIED,
  201.         ];
  202.         if (!in_array($quote->getStatus(), $allowedStatusestrue)) {
  203.             $this->addFlash('error''This quote cannot be accepted in its current state.');
  204.             return $this->redirectToRoute('quote_view_customer', ['token' => $token]);
  205.         }
  206.         /** @var LegacySubscriptionBillingService $billing */
  207.         $billing $this->get('app.legacy_subscription_billing_service');
  208.         try {
  209.             $invoice $billing->createOrReuseQuoteInvoice($quote, (int)$this->loggedUserId($request));
  210.             $service->customerAccept($quote);
  211.         } catch (\RuntimeException $e) {
  212.             $this->addFlash('error'$e->getMessage() . ' Please contact support to finish setup before payment.');
  213.             return $this->redirectToRoute('quote_view_customer', ['token' => $token]);
  214.         }
  215.         if ($quote->getPaymentType() === SubscriptionQuote::PAYMENT_AUTOMATIC) {
  216.             $this->addFlash('success''Quote accepted. Redirecting you to secure payment.');
  217.             return $this->redirectToRoute('quote_payment_redirect', [
  218.                 'token' => $token,
  219.                 'invoice_id' => $invoice->getId(),
  220.             ]);
  221.         }
  222.         $this->addFlash('success''Quote accepted. Your invoice is waiting for payment confirmation.');
  223.         return $this->redirectToRoute('quote_view_customer', ['token' => $token]);
  224.     }
  225.     /**
  226.      * POST /quote/{token}/reject
  227.      */
  228.     public function RejectQuoteAction(Request $request$token)
  229.     {
  230.         $service $this->get('app.quote_service');
  231.         $quote $service->findByToken($token);
  232.         if (!$quote) {
  233.             throw $this->createNotFoundException('Quote not found.');
  234.         }
  235.         $reason trim($request->request->get('reason'''));
  236.         $service->customerReject($quote$reason ?: null);
  237.         $this->addFlash('info''Quote rejected. Our team will be in touch if you would like to discuss further.');
  238.         return $this->redirectToRoute('quote_view_customer', ['token' => $token]);
  239.     }
  240.     /**
  241.      * GET /quote/{token}/pay/{invoice_id}
  242.      */
  243.     public function PaymentRedirectAction(Request $request$token$invoice_id)
  244.     {
  245.         $service $this->get('app.quote_service');
  246.         $quote $service->findByToken($token);
  247.         if (!$quote) {
  248.             throw $this->createNotFoundException('Quote not found.');
  249.         }
  250.         /** @var LegacySubscriptionBillingService $billing */
  251.         $billing $this->get('app.legacy_subscription_billing_service');
  252.         $invoice = (int)$invoice_id 0
  253.             $this->getDoctrine()->getManager('company_group')
  254.                 ->getRepository('CompanyGroupBundle\Entity\EntityInvoice')
  255.                 ->find((int)$invoice_id)
  256.             : $billing->findQuoteInvoice($quote);
  257.         if (!$invoice) {
  258.             throw $this->createNotFoundException('Invoice not found.');
  259.         }
  260.         $successActionData json_decode((string)$invoice->getSuccessActionData(), true);
  261.         $linkedQuoteId is_array($successActionData) ? (int)($successActionData['quoteId'] ?? 0) : 0;
  262.         if ($linkedQuoteId !== (int)$quote->getId()) {
  263.             throw $this->createAccessDeniedException('Invoice does not belong to this quote.');
  264.         }
  265.         if ($quote->getPaymentType() !== SubscriptionQuote::PAYMENT_AUTOMATIC) {
  266.             $this->addFlash('info''This quote is configured for manual payment confirmation.');
  267.             return $this->redirectToRoute('quote_view_customer', ['token' => $token]);
  268.         }
  269.         $encData $this->get('url_encryptor')->encrypt(json_encode(
  270.             $billing->buildPaymentRedirectPayload($invoice11)
  271.         ));
  272.         return $this->redirect($this->generateUrl('make_payment_of_entity_invoice', [
  273.             'encData' => $encData,
  274.         ], UrlGeneratorInterface::ABSOLUTE_URL));
  275.     }
  276. }