src/Controller/APIRest/SynerticMobileApp/SynerticMobileAppController.php line 319

Open in your IDE?
  1. <?php
  2. namespace App\Controller\APIRest\SynerticMobileApp;
  3. // Core app
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Doctrine\Persistence\ManagerRegistry;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpFoundation\JsonResponse;
  8. // Services
  9. use App\Services\APIRest\Tools\APIResponseTools;
  10. use App\Services\APIRest\SynerticMobileApp\SynerticMobileAppTools;
  11. use App\Services\APIRest\SynerticMobileApp\ServiceOrderTools;
  12. use App\Services\Log\LogMobileAppTools;
  13. use App\Services\LogTools;
  14. use App\Services\Planning\TaskTools;
  15. use App\Utils\APIError;
  16. // Exceptions
  17. use Exception;
  18. use App\Entity\APIRest\AccessAPI;
  19. use App\Entity\Planning\Task;
  20. use App\Entity\Security\Acl;
  21. use App\Enum\Log\LogLevel;
  22. use App\Exception\APIRest\APIAuthenticationException;
  23. use App\Exception\APIRest\APIException;
  24. use App\Exception\APIRest\APIInvalidDataException;
  25. use App\Exception\APIRest\APIMissingDataException;
  26. use App\Services\APIRest\Tools\GhostingTools;
  27. use App\Services\Security\AclTools;
  28. use App\Services\Config\OptionConfigTools;
  29. class SynerticMobileAppController extends AbstractController
  30. {
  31.     public function __construct(
  32.         ManagerRegistry $doctrine,
  33.         LogTools $logTools,
  34.         LogMobileAppTools $logMobileAppTools,
  35.         APIResponseTools $apiResponseTools,
  36.         SynerticMobileAppTools $synerticMobileAppTools,
  37.         ServiceOrderTools $serviceOrderTools,
  38.         GhostingTools $ghostingTools,
  39.         TaskTools $taskTools,
  40.         AclTools $aclTools,
  41.         OptionConfigTools $optionConfigTools
  42.     )
  43.     {
  44.         $this->em $doctrine->getManager();
  45.         $this->logTools $logTools;
  46.         $this->logMobileAppTools $logMobileAppTools;
  47.         $this->apiResponseTools $apiResponseTools;
  48.         $this->synerticMobileAppTools $synerticMobileAppTools;
  49.         $this->serviceOrderTools $serviceOrderTools;
  50.         $this->ghostingTools $ghostingTools;
  51.         $this->taskTools $taskTools;
  52.         $this->aclTools $aclTools;
  53.         $this->optionConfigTools $optionConfigTools;
  54.     }
  55.     
  56.     public function addFCMToken(Request $request): JsonResponse
  57.     {
  58.         try
  59.         {    
  60.             $requestedData json_decode($request->getContent(), true);
  61.             $userAppContext $this->logMobileAppTools->buildUserAppContext($requestedData);
  62.             $this->logMobileAppTools->logApiCall(
  63.                 $request->getMethod(),
  64.                 $request->getPathInfo(),
  65.                 $userAppContext
  66.             );
  67.             
  68.             if($this->getUser() instanceof AccessAPI)
  69.             {
  70.                 $accessAPI =  $this->getUser();
  71.             }
  72.             else
  73.             {
  74.                 $this->logMobileAppTools->error(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS), $userAppContext);
  75.                 throw new APIAuthenticationException(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS));
  76.             }
  77.             if (empty($requestedData))
  78.             {
  79.                 $this->logMobileAppTools->error(new APIError(APIError::INVALID_DATA), $userAppContext);
  80.                 throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  81.             }
  82.             if(!array_key_exists("fcm_token"$requestedData))
  83.             {
  84.                 $this->logMobileAppTools->error(new APIError(APIError::SYNERTIC_MISSING_FCM_TOKEN), $userAppContext);
  85.                 throw new APIInvalidDataException(new APIError(APIError::SYNERTIC_MISSING_FCM_TOKEN));
  86.             }
  87.             if($requestedData["fcm_token"] === null)
  88.             {
  89.                 $this->logMobileAppTools->error(new APIError(APIError::SYNERTIC_INVALID_FCM_TOKEN), $userAppContext);
  90.                 throw new APIInvalidDataException(new APIError(APIError::SYNERTIC_INVALID_FCM_TOKEN));
  91.             }
  92.             // Can't verify that fcm token is legit, must trust api call
  93.             $accessAPI->setFcmToken($requestedData['fcm_token']);
  94.             $this->em->persist($accessAPI);
  95.             try
  96.             {
  97.                 $this->em->flush();
  98.             }
  99.             catch (\Exception $e)
  100.             {
  101.                 $this->logMobileAppTools->critical('Database flush error'$e$userAppContext);
  102.                 $this->logMobileAppTools->error(new APIError(APIError::SYNERTIC_ERROR_SETTING_FCM_TOKEN), $userAppContext);
  103.                 throw new APIInvalidDataException(new APIError(APIError::SYNERTIC_ERROR_SETTING_FCM_TOKEN));
  104.             }
  105.             return $this->apiResponseTools->successResponse(array("status" => "success"));
  106.         }
  107.         catch (Exception $e)
  108.         {
  109.             if (!$e instanceof APIException)
  110.             {
  111.                 $this->logMobileAppTools->critical(''$e$userAppContext ?? []);
  112.                 $e = new APIException(new APIError(APIError::UNEXPECTED_ERROR));
  113.             }
  114.             $this->logMobileAppTools->error($e->getApiError(), $userAppContext ?? []);
  115.             return $this->apiResponseTools->exceptionResponse($e);
  116.         }
  117.     }
  118.     public function getFileBase64(Request $request): JsonResponse
  119.     {
  120.         try
  121.         {
  122.             $requestedData json_decode($request->getContent(), true);
  123.             $userAppContext $this->logMobileAppTools->buildUserAppContext($requestedData);
  124.             $this->logMobileAppTools->logApiCall(
  125.                 $request->getMethod(),
  126.                 $request->getPathInfo(),
  127.                 $userAppContext
  128.             );
  129.             
  130.             if($this->getUser() instanceof AccessAPI)
  131.             {
  132.                 $accessAPI $this->ghostingTools->resolveGhostedUser($requestedData$this->getUser());
  133.             }
  134.             else
  135.             {
  136.                 $this->logMobileAppTools->error(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS), $userAppContext);
  137.                 throw new APIAuthenticationException(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS));
  138.             }
  139.             if (empty($requestedData))
  140.             {
  141.                 $this->logMobileAppTools->error(new APIError(APIError::INVALID_DATA), $userAppContext);
  142.                 throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  143.             }
  144.             if (!array_key_exists('type'$requestedData))
  145.             {
  146.                 $this->logMobileAppTools->error(new APIError(APIError::SYNERTIC_FILE_BASE64_MISSING_TYPE), $userAppContext);
  147.                 throw new APIInvalidDataException(new APIError(APIError::SYNERTIC_FILE_BASE64_MISSING_TYPE));
  148.             }
  149.             if($requestedData['type'] === null)
  150.             {
  151.                 $this->logMobileAppTools->error(new APIError(APIError::SYNERTIC_FILE_BASE64_INVALID_TYPE), $userAppContext);
  152.                 throw new APIInvalidDataException(new APIError(APIError::SYNERTIC_FILE_BASE64_INVALID_TYPE));
  153.             }
  154.             $type '';
  155.             if (!empty($requestedData['type']))
  156.             {
  157.                 if (in_array($requestedData['type'], ['attachment''devis''document''invoice''credit']))
  158.                 {
  159.                     $type $requestedData['type'];
  160.                 }
  161.                 else
  162.                 {
  163.                     $this->logMobileAppTools->error(new APIError(APIError::SYNERTIC_FILE_BASE64_INVALID_TYPE), $userAppContext);
  164.                     throw new APIInvalidDataException(new APIError(APIError::SYNERTIC_FILE_BASE64_INVALID_TYPE));
  165.                 }
  166.             }
  167.             if ($type == 'devis')
  168.             {
  169.                 $devisFileBase64Data $this->synerticMobileAppTools->getDevisFileBase64($requestedData);
  170.                 return $this->apiResponseTools->successResponse($devisFileBase64Data);
  171.             }
  172.             if ($type == 'document')
  173.             {
  174.                 $documentFileBase64Data $this->synerticMobileAppTools->getDocumentFileBase64($requestedData);
  175.                 return $this->apiResponseTools->successResponse($documentFileBase64Data);
  176.             }
  177.             if ($type == 'invoice' || $type == 'credit')
  178.             {
  179.                 $invoiceFileBase64Data $this->synerticMobileAppTools->getInvoiceFileBase64($requestedData);
  180.                 return $this->apiResponseTools->successResponse($invoiceFileBase64Data);
  181.             }
  182.             if ($type == 'attachment')
  183.             {
  184.                 $attachmentFileBase64Data $this->synerticMobileAppTools->getAttachmentFileBase64($requestedData);
  185.                 return $this->apiResponseTools->successResponse($attachmentFileBase64Data);
  186.             }
  187.             
  188.             $this->logMobileAppTools->error(new APIError(APIError::INVALID_DATA), $userAppContext);
  189.             throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  190.         }
  191.         catch (Exception $e)
  192.         {
  193.             if (!$e instanceof APIException)
  194.             {
  195.                 $this->logMobileAppTools->critical(''$e$userAppContext ?? []);
  196.                 $e = new APIException(new APIError(APIError::UNEXPECTED_ERROR));
  197.             }
  198.             $this->logMobileAppTools->error($e->getApiError(), $userAppContext ?? []);
  199.             return $this->apiResponseTools->exceptionResponse($e);
  200.         }
  201.     }
  202.     public function setServiceOrderStatus(Request $request): JsonResponse
  203.     {
  204.         try
  205.         {
  206.             $requestedData json_decode($request->getContent(), true);
  207.             $userAppContext $this->logMobileAppTools->buildUserAppContext($requestedData);
  208.             $this->logMobileAppTools->logApiCall(
  209.                 $request->getMethod(),
  210.                 $request->getPathInfo(),
  211.                 $userAppContext
  212.             );
  213.             
  214.             if($this->getUser() instanceof AccessAPI)
  215.             {
  216.                 $accessAPI $this->ghostingTools->resolveGhostedUser($requestedData$this->getUser());
  217.             }
  218.             else
  219.             {
  220.                 $this->logMobileAppTools->error(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS), $userAppContext);
  221.                 throw new APIAuthenticationException(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS));
  222.             }
  223.             
  224.             $statusChangeData $this->serviceOrderTools->setServiceOrderStatus($requestedData);
  225.             return $this->apiResponseTools->successResponse($statusChangeData);
  226.         }
  227.         catch (Exception $e)
  228.         {
  229.             if (!$e instanceof APIException)
  230.             {
  231.                 $this->logMobileAppTools->critical(''$e$userAppContext ?? []);
  232.                 $e = new APIException(new APIError(APIError::UNEXPECTED_ERROR));
  233.             }
  234.             $this->logMobileAppTools->error($e->getApiError(), $userAppContext ?? []);
  235.             return $this->apiResponseTools->exceptionResponse($e);
  236.         }
  237.     }
  238.     
  239.     public function checkAppUpdate(Request $request): JsonResponse
  240.     {
  241.         try
  242.         {    
  243.             $requestedData json_decode($request->getContent(), true);
  244.             $userAppContext $this->logMobileAppTools->buildUserAppContext($requestedData);
  245.             $this->logMobileAppTools->logApiCall(
  246.                 $request->getMethod(),
  247.                 $request->getPathInfo(),
  248.                 $userAppContext
  249.             );
  250.             
  251.             if($this->getUser() instanceof AccessAPI)
  252.             {
  253.                 $accessAPI $this->ghostingTools->resolveGhostedUser($requestedData$this->getUser());
  254.             }
  255.             else
  256.             {
  257.                 $this->logMobileAppTools->error(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS), $userAppContext);
  258.                 throw new APIAuthenticationException(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS));
  259.             }
  260.             if (empty($requestedData))
  261.             {
  262.                 $this->logMobileAppTools->error(new APIError(APIError::INVALID_DATA), $userAppContext);
  263.                 throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  264.             }
  265.             $checkUpdate $this->synerticMobileAppTools->checkAppUpdate($requestedData);
  266.             return $this->apiResponseTools->successResponse($checkUpdate);
  267.         }
  268.         catch (Exception $e)
  269.         {
  270.             if (!$e instanceof APIException)
  271.             {
  272.                 $this->logMobileAppTools->critical(''$e$userAppContext ?? []);
  273.                 $e = new APIException(new APIError(APIError::UNEXPECTED_ERROR));
  274.             }
  275.             $this->logMobileAppTools->error($e->getApiError(), $userAppContext ?? []);
  276.             return $this->apiResponseTools->exceptionResponse($e);
  277.         }
  278.     }
  279.     
  280.     public function addLog(Request $request): JsonResponse
  281.     {
  282.         try
  283.         {    
  284.             $requestedData json_decode($request->getContent(), true);
  285.             $userAppContext $this->logMobileAppTools->buildUserAppContext($requestedData);
  286.             $this->logMobileAppTools->logApiCall(
  287.                 $request->getMethod(),
  288.                 $request->getPathInfo(),
  289.                 $userAppContext
  290.             );
  291.             
  292.             if($this->getUser() instanceof AccessAPI)
  293.             {
  294.                 $accessAPI $this->ghostingTools->resolveGhostedUser($requestedData$this->getUser());
  295.             }
  296.             else
  297.             {
  298.                 $this->logMobileAppTools->error(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS), $userAppContext);
  299.                 throw new APIAuthenticationException(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS));
  300.             }
  301.             if (empty($requestedData))
  302.             {
  303.                 $this->logMobileAppTools->error(new APIError(APIError::INVALID_DATA), $userAppContext);
  304.                 throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  305.             }
  306.             // Check if code is IKEA_CALL
  307.             if (isset($requestedData['code']) && $requestedData['code'] === 'IKEA_CALL')
  308.             {
  309.                 // Extract taskId from various possible locations in request data
  310.                 // The mobile app may send taskId in different formats:
  311.                 // 1. Direct: $requestedData['taskId']
  312.                 // 2. JSON string in message: $requestedData['message'] (decoded)
  313.                 // 3. Nested arrays: any array value containing 'taskId'
  314.                 // 4. JSON string in any value: any string value that decodes to array with 'taskId'
  315.                 $taskId null;
  316.                 
  317.                 // First, check if taskId is directly in requestedData
  318.                 if (isset($requestedData['taskId']))
  319.                 {
  320.                     $taskId $requestedData['taskId'];
  321.                 }
  322.                 // Otherwise, try to parse message as JSON
  323.                 elseif (isset($requestedData['message']))
  324.                 {
  325.                     $messageData json_decode($requestedData['message'], true);
  326.                     
  327.                     if ($messageData !== null && isset($messageData['taskId']))
  328.                     {
  329.                         $taskId $messageData['taskId'];
  330.                     }
  331.                 }
  332.                 // Search recursively in all request data for taskId
  333.                 if ($taskId === null)
  334.                 {
  335.                     foreach ($requestedData as $key => $value)
  336.                     {
  337.                         if (is_array($value) && isset($value['taskId']))
  338.                         {
  339.                             $taskId $value['taskId'];
  340.                             break;
  341.                         }
  342.                         elseif (is_string($value))
  343.                         {
  344.                             // Try to decode as JSON if it's a string
  345.                             $decoded json_decode($valuetrue);
  346.                             if (is_array($decoded) && isset($decoded['taskId']))
  347.                             {
  348.                                 $taskId $decoded['taskId'];
  349.                                 break;
  350.                             }
  351.                         }
  352.                     }
  353.                 }
  354.                 
  355.                 // Debug logging for IKEA hotline integration issues
  356.                 // Log full request structure when taskId extraction fails for debugging
  357.                 if ($taskId === null)
  358.                 {
  359.                     $this->logMobileAppTools->debug(
  360.                         'IKEA_CALL - requestedData keys: ' implode(', 'array_keys($requestedData)) . ' | Full data: ' json_encode($requestedData),
  361.                         $userAppContext
  362.                     );
  363.                     
  364.                     // Legacy addLog call for compatibility with existing monitoring
  365.                     $addLog $this->synerticMobileAppTools->addLog($requestedData);
  366.                     $this->logMobileAppTools->error(
  367.                         new APIError(APIError::INVALID_DATA'IKEA_CALL log missing taskId in request data or message JSON'),
  368.                         $userAppContext
  369.                     );
  370.                     throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  371.                 }
  372.                 $task $this->em->getRepository(Task::class)->find($taskId);
  373.                 if ($task === null)
  374.                 {
  375.                     $this->logMobileAppTools->error(
  376.                         new APIError(APIError::INVALID_DATA"IKEA_CALL log: Task with id $taskId not found"),
  377.                         $userAppContext
  378.                     );
  379.                     throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  380.                 }
  381.                 $access $accessAPI->getAccess();
  382.                 if ($access === null)
  383.                 {
  384.                     $this->logMobileAppTools->error(
  385.                         new APIError(APIError::INVALID_DATA'IKEA_CALL log: AccessAPI has no associated Access'),
  386.                         $userAppContext
  387.                     );
  388.                     throw new APIInvalidDataException(new APIError(APIError::INVALID_DATA));
  389.                 }
  390.                 $this->taskTools->logIkeaHotlineClick($task$access'mobile_app');
  391.                 return $this->apiResponseTools->successResponse(array("status" => "success"));
  392.             }
  393.             // Default behavior for other log codes
  394.             $addLog $this->synerticMobileAppTools->addLog($requestedData);
  395.             return $this->apiResponseTools->successResponse($addLog);
  396.         }
  397.         catch (Exception $e)
  398.         {
  399.             if (!$e instanceof APIException)
  400.             {
  401.                 $this->logMobileAppTools->critical(''$e$userAppContext ?? []);
  402.                 $e = new APIException(new APIError(APIError::UNEXPECTED_ERROR));
  403.             }
  404.             $this->logMobileAppTools->error($e->getApiError(), $userAppContext ?? []);
  405.             return $this->apiResponseTools->exceptionResponse($e);
  406.         }
  407.     }
  408.     /**
  409.      * Allow mobile app to send logs to the server.
  410.      */
  411.     public function sendLog(Request $request): JsonResponse
  412.     {
  413.         try
  414.         {
  415.             $requestedData json_decode($request->getContent(), true);
  416.             $userAppContext $this->logMobileAppTools->buildUserAppContext($requestedData);
  417.             $this->logMobileAppTools->logApiCall(
  418.                 $request->getMethod(),
  419.                 $request->getPathInfo(),
  420.                 $userAppContext
  421.             );
  422.             if (!$this->getUser() instanceof AccessAPI)
  423.             {
  424.                 $this->logMobileAppTools->error(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS), $userAppContext);
  425.                 throw new APIAuthenticationException(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS));
  426.             }
  427.             $accessAPI $this->ghostingTools->resolveGhostedUser($requestedData$this->getUser());
  428.             // Validate required fields
  429.             if (empty($requestedData['message']))
  430.             {
  431.                 $this->logMobileAppTools->error(new APIError(APIError::MISSING_INFO_DATA), $userAppContext);
  432.                 throw new APIMissingDataException(new APIError(APIError::MISSING_INFO_DATA));
  433.             }
  434.             $message $requestedData['message'];
  435.             $levelString $requestedData['level'] ?? 'INFO';
  436.             // Parse log level
  437.             $level LogLevel::fromString($levelString);
  438.             // Log based on level
  439.             $this->logMobileAppTools->log($level$message$userAppContext);
  440.             return $this->apiResponseTools->successResponse(['logged' => true]);
  441.         }
  442.         catch (Exception $e)
  443.         {
  444.             if (!$e instanceof APIException)
  445.             {
  446.                 $this->logMobileAppTools->critical(''$e$userAppContext ?? []);
  447.                 $e = new APIException(new APIError(APIError::UNEXPECTED_ERROR));
  448.             }
  449.             $this->logMobileAppTools->error($e->getApiError(), $userAppContext ?? []);
  450.             return $this->apiResponseTools->exceptionResponse($e);
  451.         }
  452.     }
  453.     /**
  454.      * Allow mobile app to get the permissions for the current access.
  455.      * The permissions are returned as an array of permission ids.
  456.      */
  457.     public function acl(Request $request): JsonResponse
  458.     {
  459.         try
  460.         {
  461.             $requestedData json_decode($request->getContent(), true) ?? [];
  462.             $userAppContext $this->logMobileAppTools->buildUserAppContext($requestedData);
  463.             $this->logMobileAppTools->logApiCall(
  464.                 $request->getMethod(),
  465.                 $request->getPathInfo(),
  466.                 $userAppContext
  467.             );
  468.             if ($this->getUser() instanceof AccessAPI)
  469.             {
  470.                 $accessAPI $this->ghostingTools->resolveGhostedUser($requestedData$this->getUser());
  471.             }
  472.             else
  473.             {
  474.                 $this->logMobileAppTools->error(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS), $userAppContext);
  475.                 throw new APIAuthenticationException(new APIError(APIError::AUTHENTICATION_UNAUTHORIZED_ACCESS));
  476.             }
  477.             $currentAccess $accessAPI->getAccess();
  478.             $currentGroup $this->getUser()->getSocietyGroup();
  479.             // gets fonction for currentAccess
  480.             $fonctionCurrentAccess $currentAccess->getFunction();
  481.             // gets acl for currentGroup
  482.             $acls $this->em->getRepository(Acl::class)->getForSocietyGroup($currentGroup);
  483.             $setPermissions = array();
  484.     
  485.             // make table of permissions for currentAccess
  486.             $aclPermissions = [];
  487.             foreach ($acls as $key => $acl)
  488.             {
  489.                 if ($acl->getValue() == true && $acl->getFunction()->getId() == $fonctionCurrentAccess->getId())
  490.                 {
  491.                     $aclPermissions[] .= $acl->getPermission()->getId();
  492.                     // $setPermissions[$acl->getFunction()->getId()][$acl->getPermission()->getId()] = 1;
  493.                 }
  494.             }
  495.             $options = [];
  496.             if($this->optionConfigTools->isActive_MobileAppArtisan($currentGroup))
  497.             {
  498.                 $options[] = "32";
  499.             }
  500.             $setPermissions = [
  501.                 'acl' => $aclPermissions,
  502.                 'options' => $options,
  503.             ];
  504.             return $this->apiResponseTools->successResponse($setPermissions);
  505.         }
  506.         catch (Exception $e)
  507.         {
  508.             if (!$e instanceof APIException)
  509.             {
  510.                 $this->logMobileAppTools->critical(''$e$userAppContext ?? []);
  511.                 $e = new APIException(new APIError(APIError::UNEXPECTED_ERROR));
  512.             }
  513.             $this->logMobileAppTools->error($e->getApiError(), $userAppContext ?? []);
  514.             return $this->apiResponseTools->exceptionResponse($e);
  515.         }
  516.     }
  517. }