src/ApplicationBundle/Modules/Api/Controller/PublicApiController.php line 151

Open in your IDE?
  1. <?php
  2. namespace ApplicationBundle\Modules\Api\Controller;
  3. use ApplicationBundle\ApplicationBundle;
  4. use ApplicationBundle\Entity\BrandCompany;
  5. use ApplicationBundle\Entity\InvProductCategories;
  6. use ApplicationBundle\Entity\InvProducts;
  7. use ApplicationBundle\Modules\Api\Constants\ApiConstants;
  8. use ApplicationBundle\Constants\GeneralConstant;
  9. use ApplicationBundle\Controller\GenericController;
  10. use ApplicationBundle\Helper\Generic;
  11. use ApplicationBundle\Interfaces\PublicApiInterface;
  12. use ApplicationBundle\Modules\Authentication\Constants\UserConstants;
  13. use ApplicationBundle\Modules\Inventory\Inventory;
  14. use Symfony\Component\HttpFoundation\JsonResponse;
  15. use Symfony\Component\HttpFoundation\Request;
  16. class PublicApiController extends GenericController implements PublicApiInterface
  17. {
  18.     public function DecodeQrAction(Request $request)
  19.     {
  20.         $qrTypes = [
  21.             '_APPLICANT_''_COMPANY_''_DOCUMENT_'
  22.         ];
  23.         $applicantId $request->request->get('applicant_id');
  24.         $encData $request->request->get('data'$request->request->get('query'''));
  25.         $getRelData $request->request->get('getRelData'1);
  26.         $qrType $request->request->get('qrType''_UNSET_');
  27.         $decryptedData = [
  28.             'relData' => [],
  29.         ];
  30.         //////decode qr data
  31.         $tvp $this->get('url_encryptor')->decrypt($encData);
  32.         $decryptedData json_decode($tvptrue);
  33.         if ($decryptedData == null$decryptedData = [];
  34.         $decryptedData['relData'] = [];
  35.         if ($qrType == '_UNSET_')
  36.             $qrType $decryptedData['qrType'] ?? '_APPLICANT_';
  37.         if ($qrType == '_APPLICANT_') {
  38.             $urlToCall GeneralConstant::HONEYBEE_CENTRAL_SERVER '/get_applicant_data_central';
  39.             $applicantId $decryptedData['applicantId'] ?? $applicantId;
  40.             $data = [
  41.                 'applicantId' => $applicantId
  42.             ];
  43.             $curl curl_init();
  44.             curl_setopt_array($curl, array(
  45.                 CURLOPT_RETURNTRANSFER => 1,
  46.                 CURLOPT_POST => 1,
  47.                 CURLOPT_URL => $urlToCall,
  48.                 CURLOPT_CONNECTTIMEOUT => 10,
  49.                 CURLOPT_SSL_VERIFYPEER => false,
  50.                 CURLOPT_SSL_VERIFYHOST => false,
  51.                 CURLOPT_HTTPHEADER => array(),
  52.                 CURLOPT_POSTFIELDS => $data
  53.             ));
  54.             $retData curl_exec($curl);
  55.             $errData curl_error($curl);
  56.             curl_close($curl);
  57.             $retDataObj json_decode($retDatatrue);
  58.             $decryptedData['centralData'] = $retDataObj['centralData'];
  59.             $decryptedData['relData'] = $retDataObj['centralData'];
  60.             return new JsonResponse($retDataObj['centralData']);
  61.         }
  62. //        return $decryptedData;
  63.         return new JsonResponse($decryptedData
  64.         );
  65.     }
  66.     public function EncodeQrAction(Request $request)
  67.     {
  68.         $data $request->request->get('data'$request->request->get('query'''));
  69.         $getRelData $request->request->get('getRelData'1);
  70.         $qrType $request->request->get('qrType''_UNSET_');
  71.         if (is_array($data) || is_object($data)) $data json_encode($data);
  72.         $encryptedData $this->get('url_encryptor')->encrypt($data);
  73.         return new JsonResponse(
  74.             $encryptedData
  75.         );
  76.     }
  77.     public function ProductSkuNextCodeAction(Request $request)
  78.     {
  79.         $em $this->getDoctrine()->getManager();
  80.         $prefix trim((string)$request->request->get('prefix'''));
  81.         $categoryId = (int)$request->request->get('category_id'0);
  82.         $currentProductId = (int)$request->request->get('product_id'0);
  83.         if ($prefix === '') {
  84.             return new JsonResponse([
  85.                 'success' => false,
  86.                 'message' => 'Category prefix is required.'
  87.             ]);
  88.         }
  89.         $qb $em->getRepository('ApplicationBundle\\Entity\\InvProducts')
  90.             ->createQueryBuilder('p')
  91.             ->select('p.skuCode')
  92.             ->where('p.skuCode IS NOT NULL')
  93.             ->andWhere('p.skuCode <> :emptySku')
  94.             ->andWhere('p.skuCode LIKE :skuPrefix')
  95.             ->setParameter('emptySku''')
  96.             ->setParameter('skuPrefix'$prefix '%');
  97.         if ($currentProductId 0) {
  98.             $qb->andWhere('p.id <> :currentProductId')
  99.                 ->setParameter('currentProductId'$currentProductId);
  100.         }
  101.         $skuRows $qb->getQuery()->getArrayResult();
  102.         $maxSerial 0;
  103.         foreach ($skuRows as $skuRow) {
  104.             $skuCode trim((string)($skuRow['skuCode'] ?? ''));
  105.             if ($skuCode === '') {
  106.                 continue;
  107.             }
  108.             if (preg_match('/^' preg_quote($prefix'/') . '(\d+)$/'$skuCode$matches)) {
  109.                 $serial = (int)$matches[1];
  110.                 if ($serial $maxSerial) {
  111.                     $maxSerial $serial;
  112.                 }
  113.             }
  114.         }
  115.         return new JsonResponse([
  116.             'success' => true,
  117.             'prefix' => $prefix,
  118.             'category_id' => $categoryId,
  119.             'nextNumber' => $maxSerial 1,
  120.             'skuCode' => $prefix . ($maxSerial 1),
  121.         ]);
  122.     }
  123.     public function ConvertMlParsedProductInfoToLocalAction(Request $request$id)
  124.     {
  125.         $systemType $this->container->getParameter('system_type') ?: '_ERP_';
  126.         $em_goc $this->getDoctrine()->getManager();
  127.         $em $this->getDoctrine()->getManager();
  128.         $success true;
  129.         $data $request->request->get('data'Inventory::GetImmutableKeysForProductSyncFromCentralToLocal());
  130.         if ($systemType == '_ERP_') {
  131.             //first send to central server to see if match found
  132.             $urlToCall GeneralConstant::HONEYBEE_CENTRAL_SERVER '/api/convertMlParsedProductInfoToLocal';
  133.             $curl curl_init();
  134.             curl_setopt_array($curl, [
  135.                 CURLOPT_RETURNTRANSFER => true,
  136.                 CURLOPT_POST => true,
  137.                 CURLOPT_URL => $urlToCall,
  138.                 CURLOPT_CONNECTTIMEOUT => 10,
  139.                 CURLOPT_SSL_VERIFYPEER => false,
  140.                 CURLOPT_SSL_VERIFYHOST => false,
  141.                 CURLOPT_HTTPHEADER => [],
  142.                 CURLOPT_POSTFIELDS => http_build_query([
  143.                     'data' => $data
  144.                 ])
  145.             ]);
  146.             $retData curl_exec($curl);
  147.             $errData curl_error($curl);
  148.             curl_close($curl);
  149.             if ($errData) {
  150.                 return new JsonResponse(['success' => false'data' => []]);
  151.             }
  152.             $retData json_decode($retDatatrue);
  153.             $data $retData['data'];
  154.             $em $this->getDoctrine()->getManager('company_group');
  155.             $em->getConnection()->connect();
  156.             $connected $em->getConnection()->isConnected();
  157.             $session $request->getSession();
  158.             $appIdFromSession $session->get(UserConstants::USER_APP_ID0);
  159.             $gocDataList = [];
  160.             $gocDataListByAppId = [];
  161.             $retDataDebug = array();
  162.             $appId $request->get('appId'$appIdFromSession);
  163.             if ($connected) {
  164.                 $findByQuery = array(
  165.                     'active' => 1
  166.                 );
  167.                 if ($appId !== '_UNSET_')
  168.                     $findByQuery['appId'] = $appId;
  169.                 $entry $this->getDoctrine()->getManager('company_group')
  170.                     ->getRepository("CompanyGroupBundle\\Entity\\CompanyGroup")
  171.                     ->findOneBy($findByQuery);
  172.                 $d = array(
  173.                     'name' => $entry->getName(),
  174.                     'id' => $entry->getId(),
  175.                     'image' => $entry->getImage(),
  176.                     'companyGroupHash' => $entry->getCompanyGroupHash(),
  177.                     'dbName' => $entry->getDbName(),
  178.                     'dbUser' => $entry->getDbUser(),
  179.                     'dbPass' => $entry->getDbPass(),
  180.                     'dbHost' => $entry->getDbHost(),
  181.                     'appId' => $entry->getAppId(),
  182.                     'companyRemaining' => $entry->getCompanyRemaining(),
  183.                     'companyAllowed' => $entry->getCompanyAllowed(),
  184.                 );
  185.                 $gocDataList[$entry->getId()] = $d;
  186.                 $gocId $entry->getId();
  187.                 $gocDataListByAppId[$entry->getAppId()] = $d;
  188.                 $connector $this->container->get('application_connector');
  189.                 $connector->resetConnection(
  190.                     'default',
  191.                     $gocDataList[$gocId]['dbName'],
  192.                     $gocDataList[$gocId]['dbUser'],
  193.                     $gocDataList[$gocId]['dbPass'],
  194.                     $gocDataList[$gocId]['dbHost'],
  195.                     $reset true);
  196.                 $em $this->getDoctrine()->getManager();
  197.                 $data Inventory::TryAndMatchParsedProductData($em$em_goc$systemType$data);
  198.             }
  199.         } // CENTRAL SYSTEM PROCESSING
  200.         else if ($systemType == '_CENTRAL_') {
  201.             $data Inventory::TryAndMatchParsedProductData($em$em_goc$systemType$data);
  202.         }
  203.         return new JsonResponse([
  204.             'success' => $success,
  205.             'data' => $data,
  206.         ]);
  207.     }
  208.     public function SyncItemGroupAction(Request $request$id)
  209.     {
  210.         $systemType $this->container->getParameter('system_type') ?: '_ERP_';
  211.         $em_goc $this->getDoctrine()->getManager();
  212.         $em $this->getDoctrine()->getManager();
  213.         $success true;
  214.         $data $request->request->get('data', [
  215.             'mode' => "UPDATE",
  216.             'igData' => [
  217.                 'ids' => [$id],
  218.                 'globalIds' => [0],
  219.                 'indexBy' => 'globalId',  //or id
  220.                 'info' => [
  221.                     $id => [
  222.                         "id" => $id,
  223.                         "globalId" => 0,
  224.                     ]
  225.                 ]
  226.             ],
  227.             'brandData' => [
  228.             ],
  229.         ]);
  230.         if ($systemType == '_ERP_') {
  231.             $em $this->getDoctrine()->getManager('company_group');
  232.             $em->getConnection()->connect();
  233.             $connected $em->getConnection()->isConnected();
  234.             $gocDataList = [];
  235.             $gocDataListByAppId = [];
  236.             $retDataDebug = array();
  237.             $appIds $request->get('appIds''_UNSET_');
  238.             if ($connected) {
  239.                 $findByQuery = array(
  240.                     'active' => 1
  241.                 );
  242.                 if ($appIds !== '_UNSET_')
  243.                     $findByQuery['appId'] = $appIds;
  244.                 $gocList $this->getDoctrine()->getManager('company_group')
  245.                     ->getRepository("CompanyGroupBundle\\Entity\\CompanyGroup")
  246.                     ->findBy($findByQuery);
  247.                 foreach ($gocList as $entry) {
  248.                     $d = array(
  249.                         'name' => $entry->getName(),
  250.                         'id' => $entry->getId(),
  251.                         'image' => $entry->getImage(),
  252.                         'companyGroupHash' => $entry->getCompanyGroupHash(),
  253.                         'dbName' => $entry->getDbName(),
  254.                         'dbUser' => $entry->getDbUser(),
  255.                         'dbPass' => $entry->getDbPass(),
  256.                         'dbHost' => $entry->getDbHost(),
  257.                         'appId' => $entry->getAppId(),
  258.                         'companyRemaining' => $entry->getCompanyRemaining(),
  259.                         'companyAllowed' => $entry->getCompanyAllowed(),
  260.                     );
  261.                     $gocDataList[$entry->getId()] = $d;
  262.                     $gocDataListByAppId[$entry->getAppId()] = $d;
  263.                 }
  264.                 foreach ($gocDataList as $gocId => $entry) {
  265.                     $connector $this->container->get('application_connector');
  266.                     $connector->resetConnection(
  267.                         'default',
  268.                         $gocDataList[$gocId]['dbName'],
  269.                         $gocDataList[$gocId]['dbUser'],
  270.                         $gocDataList[$gocId]['dbPass'],
  271.                         $gocDataList[$gocId]['dbHost'],
  272.                         $reset true);
  273.                     $em $this->getDoctrine()->getManager();
  274.                     Inventory::SyncItemGroup($em$em_goc$systemType$data);
  275.                 }
  276.             }
  277.         } // CENTRAL SYSTEM PROCESSING
  278.         else if ($systemType == '_CENTRAL_') {
  279.             $data Inventory::SyncItemGroup($em$em_goc$systemType$data);
  280.         }
  281.         return new JsonResponse([
  282.             'success' => $success,
  283.             'data' => $data,
  284.         ]);
  285.     }
  286.     public function CreateProductSpecAction(Request $request)
  287.     {
  288.         $em $this->getDoctrine()->getManager();
  289.         if ($request->isMethod('POST')) {
  290.             $systemType $this->container->getParameter('system_type') ?: '_ERP_';
  291.             $spec_data Inventory::CreateProductSpecification($this->getDoctrine()->getManager(),
  292.                 $systemType,
  293.                 $request->request->get('name''_UNSET_'),
  294.                 $request->request->get('unitText''_UNSET_'),
  295.                 $request->request->get('uniqueHash''_UNSET_'),
  296.                 $request->request->get('markers''_UNSET_'),
  297.                 $request->request->get('tags''_UNSET_'),
  298.                 $request->request->get('globalId'0),
  299.                 $request->getSession()->get(UserConstants::USER_LOGIN_ID));
  300.             if ($spec_data['id'] != '')
  301.                 return new JsonResponse(array("success" => true'spec_data' => $spec_data));
  302.         }
  303.         return new JsonResponse(array("success" => false'spec_data' => []));
  304. //        return $this->redirectToRoute("create_product");
  305.     }
  306.     public function getIgBrandSpecListAction(Request $request$id)
  307.     {
  308.         $systemType $this->container->getParameter('system_type') ?: '_ERP_';
  309.         $em_goc $this->getDoctrine()->getManager();
  310.         $em $this->getDoctrine()->getManager();
  311.         $data = array(
  312.             'igList' => [],
  313.             'specList' => [],
  314.         );
  315.         $igs $em
  316.             ->getRepository("ApplicationBundle\\Entity\\InvItemGroup")
  317.             ->findBy(
  318.                 array(
  319.                     'type' => 1
  320.                 )
  321.             );
  322.         foreach ($igs as $ig) {
  323.             $data['igList'][] = array(
  324.                 'id' => $ig->getId(),
  325.                 'name' => $ig->getName(),
  326.             );
  327.         }
  328.         $specs $em
  329.             ->getRepository("ApplicationBundle\\Entity\\SpecType")
  330.             ->findAll();
  331.         foreach ($specs as $spec) {
  332.             $data['specList'][] = array(
  333.                 'id' => $spec->getId(),
  334.                 'name' => $spec->getName(),
  335.                 'unitText' => $spec->getUnitText(),
  336.                 'uniqueHash' => $spec->getUniqueHash(),
  337.             );
  338.         }
  339.         return new JsonResponse([
  340.             'success' => true,
  341.             'data' => $data,
  342.         ]);
  343.     }
  344.     public function SyncProductFromCentralAction(Request $request)
  345.     {
  346.         $systemType $this->container->getParameter('system_type') ?: '_ERP_';
  347.         if ($systemType !== '_ERP_') {
  348.             return new JsonResponse(['success' => false'message' => 'Not an ERP system'], 403);
  349.         }
  350.         $globalId = (int) $request->request->get('globalId'0);
  351.         $data     $request->request->get('data', []);
  352.         if (!is_array($data)) { $data = []; }
  353.         if (!$globalId) {
  354.             return new JsonResponse(['success' => false'message' => 'globalId is required'], 400);
  355.         }
  356.         // The central "Push" is authoritative — overwrite even locally-edited fields.
  357.         $force = (int) $request->request->get('force'1) === 1;
  358.         // This server hosts MANY tenants. Apply the sync to every local tenant that
  359.         // holds the product. Each tenant DB is localhost to THIS server, so we just try
  360.         // every active tenant in the registry: those whose DB exists here update; the
  361.         // rest (other servers' tenants) fail to connect and are skipped harmlessly —
  362.         // which is also why we DON'T filter by server address (that filter was too
  363.         // brittle and could exclude this server's own tenants). Previously only the
  364.         // single default tenant was touched, so most tenants here never got the change.
  365.         $updated 0$checked 0$perTenant = [];
  366.         $gocList = [];
  367.         try {
  368.             $em_goc  $this->getDoctrine()->getManager('company_group');
  369.             $gocList $em_goc->getRepository('CompanyGroupBundle\\Entity\\CompanyGroup')->findBy(['active' => 1]);
  370.         } catch (\Throwable $e) { $gocList = []; }
  371.         if (!empty($gocList)) {
  372.             $connector $this->container->get('application_connector');
  373.             foreach ($gocList as $entry) {
  374.                 $dbName $entry->getDbName();
  375.                 if (!$dbName) { continue; }
  376.                 $checked++;
  377.                 try {
  378.                     $connector->resetConnection('default'$dbName$entry->getDbUser(), $entry->getDbPass(), $entry->getDbHost(), true);
  379.                     $tem $this->getDoctrine()->getManager();
  380.                     $tem->clear();
  381.                     $res Inventory::SyncProductFromCentral($tem$globalId$data$force);
  382.                     if (!empty($res['updatedCount'])) { $updated++; $perTenant[$dbName] = (int) $res['updatedCount']; }
  383.                 } catch (\Throwable $e) { /* DB not on this server / unreachable — skip */ }
  384.             }
  385.             try { $connector->resetConnectionToDefault('default'true); } catch (\Throwable $e) { /* ignore */ }
  386.         } else {
  387.             // fallback: no tenant registry — update the default tenant only
  388.             $res Inventory::SyncProductFromCentral($this->getDoctrine()->getManager(), $globalId$data$force);
  389.             if (!empty($res['updatedCount'])) { $updated 1; }
  390.             $perTenant['(default)'] = $res;
  391.         }
  392.         return new JsonResponse([
  393.             'success'         => true,
  394.             'tenants_checked' => $checked,
  395.             'tenants_updated' => $updated,
  396.             'results'         => $perTenant,
  397.         ], 200);
  398.     }
  399.     /**
  400.      * Receive a bulk global-ID remap from the central server after a product merge:
  401.      * for each {oldGlobalId => newGlobalId}, re-point local products that reference the old global id.
  402.      * Idempotent (re-running with the same map is a no-op). _ERP_-only; guarded by a shared secret.
  403.      */
  404.     public function RemapGlobalIdAction(Request $request)
  405.     {
  406.         $systemType $this->container->getParameter('system_type') ?: '_ERP_';
  407.         if ($systemType === '_CENTRAL_') {
  408.             return new JsonResponse(['success' => false'message' => 'Not applicable on central'], 403);
  409.         }
  410.         // Shared-secret guard — this call rewrites tenant data, so it must be authenticated.
  411.         $expected $this->container->hasParameter('central_sync_secret') ? (string) $this->container->getParameter('central_sync_secret') : '';
  412.         $provided = (string) ($request->headers->get('X-Central-Secret') ?: $request->request->get('secret'''));
  413.         if ($expected === '' || !hash_equals($expected$provided)) {
  414.             return new JsonResponse(['success' => false'message' => 'Unauthorized'], 401);
  415.         }
  416.         $mapRaw $request->request->get('map''');
  417.         $map is_array($mapRaw) ? $mapRaw json_decode((string) $mapRawtrue);
  418.         if (!is_array($map) || empty($map)) {
  419.             return new JsonResponse(['success' => false'message' => 'map (oldGlobalId => newGlobalId) is required'], 400);
  420.         }
  421.         $conn $this->getDoctrine()->getManager()->getConnection();
  422.         $remapped 0;
  423.         $pairs 0;
  424.         foreach ($map as $old => $new) {
  425.             $old = (int) $old;
  426.             $new = (int) $new;
  427.             if ($old <= || $new <= || $old === $new) {
  428.                 continue;
  429.             }
  430.             $pairs++;
  431.             try {
  432.                 $remapped += (int) $conn->executeStatement(
  433.                     "UPDATE inv_products SET global_id = :new WHERE global_id = :old",
  434.                     ['new' => $new'old' => $old]
  435.                 );
  436.             } catch (\Throwable $e) { /* skip a bad pair, keep going */ }
  437.         }
  438.         return new JsonResponse(['success' => true'pairs' => $pairs'productsRemapped' => $remapped]);
  439.     }
  440.     public function RetryFailedProductSyncAction(Request $request)
  441.     {
  442.         $systemType $this->container->getParameter('system_type') ?: '_ERP_';
  443.         if ($systemType !== '_ERP_') {
  444.             return new JsonResponse(['success' => false'message' => 'Not an ERP system'], 403);
  445.         }
  446.         $em     $this->getDoctrine()->getManager();
  447.         $result Inventory::RetrySyncFailedProducts($em, [], $systemType);
  448.         return new JsonResponse(['success' => true] + $result);
  449.     }
  450.     public function ProductDependenciesAction(Request $request)
  451.     {
  452.         $em $this->getDoctrine()->getManager();
  453.         $productId = (int) $request->request->get('product_id'$request->request->get('productId'0));
  454.         $qty = (float) $request->request->get('qty'0);
  455.         $result = array();
  456.         if ($productId <= || $qty <= 0) {
  457.             return new JsonResponse(array(
  458.                 'success' => true,
  459.                 'data' => $result,
  460.             ));
  461.         }
  462.         $dependencies $em->getRepository('ApplicationBundle\\Entity\\InvProductDependencies')->findBy(
  463.             array(
  464.                 'parentProductId' => $productId,
  465.             ),
  466.             array(
  467.                 'id' => 'ASC',
  468.             )
  469.         );
  470.         foreach ($dependencies as $dependency) {
  471.             $childProductId = (int) $dependency->getChildProductId();
  472.             if ($childProductId <= || $childProductId === $productId) {
  473.                 continue;
  474.             }
  475.             $repeatationThreshold = (int) $dependency->getRepeatationThreshold();
  476.             if ($repeatationThreshold <= 0) {
  477.                 $repeatationThreshold 1;
  478.             }
  479.             $multiplier floor($qty $repeatationThreshold);
  480.             if ($multiplier <= 0) {
  481.                 continue;
  482.             }
  483.             $repeatQty $dependency->getRepeatQty();
  484.             if ($repeatQty !== null && $repeatQty !== '') {
  485.                 $finalQty $multiplier * (float) $repeatQty;
  486.             } else {
  487.                 $finalQty $multiplier $qty;
  488.             }
  489.             if ($finalQty <= 0) {
  490.                 continue;
  491.             }
  492.             $result[] = array(
  493.                 'product_id' => $childProductId,
  494.                 'qty' => $finalQty,
  495.                 'required' => $dependency->getRequired() ? 0,
  496.             );
  497.         }
  498.         return new JsonResponse(array(
  499.             'success' => true,
  500.             'data' => $result,
  501.         ));
  502.     }
  503. }