src/Controller/Api/v3/ProjectOrderController.php line 65

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Api\v3;
  3. use App\Controller\Api\Service\EmployeeActivityService;
  4. use App\Controller\Api\Service\ProjectOrderService;
  5. use App\Controller\Api\Service\ReferenceService;
  6. use App\Entity\ContactPersonPositions;
  7. use App\Entity\ProjectOrders;
  8. use App\Entity\ProjectOrderTaskJobs;
  9. use App\Entity\ProjectOrderUndertakings;
  10. use App\Entity\Projects;
  11. use App\Entity\WorkerTimesheet;
  12. use App\Enum\SeverityInterface;
  13. use App\Service\Calculators\ProjectOrderCalculator;
  14. use App\Service\SerializeService\EmployeeSerialize;
  15. use App\Service\SerializeService\ExpenseSerialize;
  16. use App\Service\SerializeService\ProjectOrderSerialize;
  17. use App\Service\SerializeService\ProjectSerialize;
  18. use App\Service\SerializeService\ReferenceSerialize;
  19. use App\Service\StreamingService;
  20. use App\Service\WorksPlan\WorksPlanMaterializer;
  21. use Doctrine\ORM\EntityManagerInterface;
  22. use phpDocumentor\Reflection\Types\This;
  23. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  24. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\HttpFoundation\Response;
  27. use Symfony\Component\HttpFoundation\StreamedResponse;
  28. use Symfony\Component\Routing\Annotation\Route;
  29. use Symfony\Component\Serializer\SerializerInterface;
  30. use Symfony\Contracts\Translation\TranslatorInterface;
  31. /**
  32.  * @Route("/api/v3/project-order")
  33.  */
  34. class ProjectOrderController extends AbstractController
  35. {
  36.     /**
  37.      * @Route("/orders/{project_id}", name="api_v3_project_order_orders", methods={"POST"}, requirements={"project_id": "\d+"} ,defaults={"project_id": null} )
  38.      * @ParamConverter("project", options={"mapping":{"project_id": "id"}})
  39.      * @throws \Exception
  40.      */
  41.     public function orders( ?Projects $projectProjectSerialize $projectSerializeProjectOrderService $projectOrderServiceProjectOrderSerialize $projectOrderSerializeTranslatorInterface $translator,
  42.             Request $request,
  43.              ?int $project_id): Response
  44.     {
  45.         if( $project_id && !$project ){
  46.             return $this->json([
  47.                 "message" => $translator->trans('Project not found'),
  48.                 "severity" => SeverityInterface::ERROR
  49.             ], Response::HTTP_NOT_FOUND);
  50.         }
  51.         // $orders = $project->getProjectOrders();
  52.         // ℹ️ℹ️ 💀💀 This is a old, By New Version success with Task/Jobs Weight!!! 💀💀 ℹ️ℹ️
  53.         // $orders = $projectOrderService->setEntity($project)->findByFeedbackStatus($request->request->get('type'), $range['from'], $range['to']);
  54.         // Project Based | All
  55.         if($project){
  56.             $projectOrderService->setProject($project);
  57.         }
  58.         $orders $projectOrderService->setParams($request->request)->fetchAll();
  59.         // No need use the service, while project delivered by Dependency Injection
  60.         $serializedProjectOrders $projectOrderSerialize->setEntity($orders)->serializeListNested();
  61.         $projectBase $projectSerialize->setEntity($project)->serializeBase();
  62.         return $this->json([
  63.             "message" => $translator->trans('Loaded project orders successfully'),
  64.             "severity" => SeverityInterface::SUCCESS,
  65.             // TODO Remove Two of One
  66.             "projectOrders" => $serializedProjectOrders,
  67.             "orders" => $serializedProjectOrders,
  68.             "projectBase" => $projectBase
  69.         ], Response::HTTP_OK );
  70.     }
  71.     /**
  72.      * Serialize with minimal groups
  73.      * @Route("/smart-orders/{project_id}", name="api_v3_project_order_smart_orders", methods={"POST"}, requirements={"project_id": "\d+"} ,defaults={"project_id": null} )
  74.      * @ParamConverter("project", options={"mapping":{"project_id": "id"}})
  75.      * @throws \Exception
  76.      */
  77.     public function smartOrders( ?Projects $projectProjectOrderService $projectOrderServiceProjectOrderSerialize $projectOrderSerializeTranslatorInterface $translator, ?int $project_id): Response
  78.     {
  79.         if( $project_id && !$project ){
  80.             return $this->json([
  81.                 "message" => $translator->trans('Project not found'),
  82.                 "severity" => SeverityInterface::ERROR
  83.             ], Response::HTTP_NOT_FOUND);
  84.         }
  85.         // Project Based | All
  86.         if($project){
  87.             $projectOrderService->setProject($project);
  88.         }
  89.         $orders $projectOrderService->fetchAll();
  90.         // No need use the service, while project delivered by Dependency Injection
  91.         $serializedProjectOrders $projectOrderSerialize->setEntity($orders)->serializeForSelector();
  92.         return $this->json([
  93.             "message" => $translator->trans('Loaded project orders successfully'),
  94.             "severity" => SeverityInterface::SUCCESS,
  95.             "orders" => $serializedProjectOrders,
  96.         ], Response::HTTP_OK );
  97.     }
  98.     /**
  99.      * Streamed Order
  100.      * @Route("/streamed-fetch/{project_id}/{order_id}", name="api_v3_project_order_streamed_fetch", requirements={"project_id": "\d+", "order_id": "\d+"}, defaults={"order_id": null} )
  101.      * @ParamConverter("project", options={"mapping":{"project_id": "id"}})
  102.      * @ParamConverter("projectOrder", options={"mapping":{"order_id": "id"}})
  103.      */
  104.     public function streamedFetch( ?Projects $project, ?ProjectOrders $projectOrder,
  105.                                    ProjectOrderSerialize $projectOrderSerialize,
  106.                                    ReferenceSerialize $referenceSerialize,
  107.                                    ReferenceService $referenceService,
  108.                                    EmployeeActivityService $employeeActivityServiceEmployeeSerialize $employeeSerializeProjectSerialize $projectSerialize,
  109.                                 TranslatorInterface $translator,
  110.                                 WorksPlanMaterializer $worksPlanMaterializer,
  111.                                 EntityManagerInterface $manager
  112.     ): StreamedResponse
  113.     {
  114.         // Plana bağlı order → AÇILIŞTA plandan SYNC (execution-preserve). Stream BAŞLAMADAN,
  115.         // normal request bağlamında yap (mid-stream flush + back-sync yan etkileri SSE'yi kırıyordu).
  116.         // Plan boşsa (task yok / kaydedilmemiş) order'ı silme → guard.
  117.         if ($projectOrder !== null) {
  118.             $boundPlan $projectOrder->getWorksPlan();
  119.             if ($boundPlan !== null && count($boundPlan->getTasks()) > 0) {
  120.                 $worksPlanMaterializer->materialize($boundPlan);
  121.                 $manager->flush();
  122.             }
  123.         }
  124.         $streaming = new StreamingService([
  125.             "localText" => ["completed" => $translator->trans('Completed')]
  126.         ]);
  127.         $streaming->appendStream(
  128.             'employees',
  129.             $translator->trans('Loading employee...'),
  130.             fn() => $employeeSerialize->setEntity($employeeActivityService->fetchActiveEmployees())->setGroups([
  131.                 "worker_activity@base",
  132.                 "worker_activity@worker",
  133.                 "worker@base",
  134.                 "worker_activity@partner",
  135.                 "partner@base"
  136.             ])->render()
  137.         );
  138.         $streaming->appendStream(
  139.             'invoiceTypes',
  140.             $translator->trans('Loading invoice Types...'),
  141.             fn() => $referenceSerialize->setEntity($referenceService->invoiceTypes())->setGroups([
  142.                 "invoice_type@base"
  143.             ])->render()
  144.         );
  145.         $streaming->appendStream(
  146.             'project',
  147.             $translator->trans('Loading project...'),
  148.             function () use ($project$projectSerialize) {
  149.                 if (!$project) {
  150.                     throw new \DomainException('Project not found');
  151.                 }
  152.                 return $projectSerialize->setEntity($project)->setGroups([
  153.                     "project@core",
  154.                     "project@owner",
  155.                     "project_owner@base",
  156.                     "project_owner@contact_persons",
  157.                     "contact_person@core",
  158.                     "contact_person@positions",
  159.                     "contact_person_position@base",
  160.                     "project@branches",
  161.                     "branch@core",
  162.                     "branch@departments",
  163.                     "branch_department@core",
  164.                     "project@invoice_type",
  165.                     "invoice_type@base",
  166.                     "project@settings",
  167.                     "project_setting@core"
  168.                 ])->render();
  169.             }
  170.         );
  171.         $streaming->appendStream(
  172.             'corporateHierarchy',
  173.             $translator->trans('Loading Corporate Hierarchy...'),
  174.             function () use ($referenceService$referenceSerialize) {
  175.                 $corporate_hierarchy $referenceService->setClassName(ContactPersonPositions::class)->fetch();
  176.                 return $referenceSerialize->setEntity($corporate_hierarchy)->setGroups([
  177.                     "contact_person_position@base"
  178.                 ])->render();
  179.             }
  180.         );
  181.         $streaming->appendStream(
  182.             'projectOrder',
  183.             $translator->trans('Loading project order...'),
  184.             function() use ($projectOrder$projectOrderSerialize){
  185.                 if(!$projectOrder){
  186.                     return null;
  187.                 }
  188.                 // Sync yukarıda (stream öncesi) yapıldı; burada sadece serialize.
  189.                 return $projectOrderSerialize->setEntity($projectOrder)->serializeFull();
  190.             }
  191.         );
  192.         return $streaming->flush();
  193.     }
  194.     /**
  195.      * @Route("/fetch/{order_id}", name="api_v3_project_order_fetch", methods={"POST"}, defaults={"order_id": null})
  196.      * @ParamConverter("projectOrder", options={"mapping":{"order_id": "id"}})
  197.      * @throws \Exception
  198.      */
  199.     public function fetch( ?ProjectOrders $projectOrderProjectOrderSerialize $projectOrderSerializeTranslatorInterface $translator, ?int $order_id null ): Response {
  200.         if($order_id && !$projectOrder){
  201.             return $this->json([
  202.                 "message" => $translator->trans("Order not found!"),
  203.                 "severity" => SeverityInterface::WARNING
  204.             ], Response::HTTP_NOT_FOUND );
  205.         }
  206.         $orderSerialized $projectOrderSerialize->setEntity($projectOrder)->serializeFull();
  207.         return $this->json([
  208.             "message" => $translator->trans('Order fetched successfully'),
  209.             "severity" => SeverityInterface::SUCCESS,
  210.             "projectOrder" => $orderSerialized
  211.         ]);
  212.     }
  213.     /**
  214.      * Tek job için tamamlama uygunluğu (live kontrol) — checkbox/Zeitachse öncesi.
  215.      * @Route("/job-completion-check/{job_id}", name="api_v3_project_order_job_completion_check", methods={"POST"}, requirements={"job_id": "\d+"})
  216.      * @ParamConverter("job", options={"mapping":{"job_id": "id"}})
  217.      */
  218.     public function jobCompletionCheck( ?ProjectOrderTaskJobs $jobProjectOrderService $projectOrderServiceTranslatorInterface $translatorint $job_id ): Response {
  219.         if (!$job) {
  220.             return $this->json([
  221.                 "message" => $translator->trans('Job not found'),
  222.                 "severity" => SeverityInterface::ERROR
  223.             ], Response::HTTP_NOT_FOUND);
  224.         }
  225.         $result $projectOrderService->checkJobCompletion($job);
  226.         return $this->json([
  227.             "allowed" => $result['allowed'],
  228.             "start_at" => $result['start_at'],
  229.             "message" => $result['message'],
  230.             "severity" => $result['allowed'] ? SeverityInterface::SUCCESS SeverityInterface::WARNING
  231.         ]);
  232.     }
  233.     /**
  234.      * @Route("/upsert/{project_id}/{order_id}", name="api_v3_project_order_handle", methods={"POST"}, requirements={"project_id": "\d+"}, defaults={"order_id": null})
  235.      * @ParamConverter("project", options={"mapping":{"project_id": "id"}})
  236.      * @ParamConverter("projectOrder", options={"mapping":{"order_id": "id"}})
  237.      * @throws \Exception
  238.      */
  239.     public function upsert( ?Projects $project, ?ProjectOrders $projectOrderProjectOrderService $projectOrderServiceProjectOrderSerialize $projectOrderSerializeRequest $requestTranslatorInterface $translator, ?int $order_id null ): Response {
  240.         $projectOrder $projectOrderService
  241.             ->setEntity($projectOrder$order_id)
  242.             ->setParams($request->request)
  243.             ->upsert($project);
  244.         if($projectOrderService->getException()){
  245.             return $this->json([
  246.                 "message" => $projectOrderService->getException()->getMessage(),
  247.                 "severity" => SeverityInterface::WARNING
  248.             ], $projectOrderService->getException()->getCode() );
  249.         }
  250.         $projectOrderSerialized $projectOrderSerialize->setEntity($projectOrder)->serializeFull();
  251. //        echo '<pre>';
  252. //        print_r($projectOrderSerialized);
  253. //        echo '</pre>';
  254. //        dd();
  255.         return $this->json([
  256.             "message" => $translator->trans('Order managed successfully'),
  257.             "severity" => SeverityInterface::SUCCESS,
  258.             "projectOrder" => $projectOrderSerialized
  259.         ]);
  260.     }
  261.     /**
  262.      * @Route("/fetch-order-billings/{order_id}", name="api_v3_project_order_fetch_billings", methods={"POST"}, requirements={"order_id": "\d+"}, defaults={"order_id": null} )
  263.      * @ParamConverter("project", options={"mapping":{"project_id": "id"}})
  264.      * @ParamConverter("projectOrder", options={"mapping":{"order_id": "id"}})
  265.      * @throws \Exception
  266.      */
  267.     public function fetchOrderBillings( ?ProjectOrders $projectOrderProjectOrderSerialize $projectOrderSerializeReferenceSerialize $referenceSerializeTranslatorInterface $translatorRequest $requestint $order_id ): Response
  268.     {
  269.         if ($order_id && !$projectOrder) {
  270.             return $this->json([
  271.                 "message"  => $translator->trans('Project order not found'),
  272.                 "severity" => SeverityInterface::ERROR
  273.             ], Response::HTTP_NOT_FOUND);
  274.         }
  275.         $billings $projectOrder->getOrderBillings();
  276.         // Deep note: serializeFull() is scoped to the aggregate root (Billings),
  277.         // so only billing fields and their owned relations are included.
  278.         $serializedProjectOrderBillings $projectOrderSerialize->setEntity($billings)->serializeFull();
  279.         $serializedPaymentStatus $referenceSerialize->serializedInvoicePaymentStatus();
  280.         return $this->json([
  281.             "message" => $translator->trans('Loaded project orders successfully'),
  282.             "severity" => SeverityInterface::SUCCESS,
  283.             "billings" => $serializedProjectOrderBillings,
  284.             "masterData" => [
  285.                 "paymentStatuses" => $serializedPaymentStatus
  286.             ]
  287.         ], Response::HTTP_OK );
  288.     }
  289.     /**
  290.      * @deprecated @see in Billings Service
  291.      * @Route("/upsert-billings/{order_id}", name="api_v3_project_order_upsert_billings", methods={"POST"}, requirements={"order_id": "\d+"}, defaults={"voucher_id": null} )
  292.      * @ParamConverter("projectOrder", options={"mapping":{"order_id": "id"}})
  293.      * @ParamConverter("undertaking", options={"mapping":{"voucher_id": "id"}})
  294.      * @throws \Exception
  295.      */
  296.     public function upsertBillings(
  297.         ?ProjectOrders $projectOrder,
  298.         ProjectOrderService $projectOrderService,
  299.         ProjectOrderSerialize $projectOrderSerialize,
  300.         TranslatorInterface $translator,
  301.         Request $request,
  302.         int $order_id,
  303.         ?int $voucher_id ): Response
  304.     {
  305.         return $this->json([
  306.             "message" => $translator->trans('This route not more efficient, please use direct in/on Billings Service'),
  307.             "severity" => SeverityInterface::WARNING
  308.         ], Response::HTTP_UNAUTHORIZED );
  309.     }
  310.     /**
  311.      * TODO Remove Forever
  312.      * @deprecated
  313.      * @see upsertBillings in this controller
  314.      * @Route("/upsert-voucher/{order_id}/{voucher_id}", name="api_v3_project_order_upsert_voucher", methods={"POST"}, requirements={"order_id": "\d+"}, defaults={"voucher_id": null} )
  315.      * @ParamConverter("projectOrder", options={"mapping":{"order_id": "id"}})
  316.      * @ParamConverter("undertaking", options={"mapping":{"voucher_id": "id"}})
  317.      * @throws \Exception
  318.      */
  319.     public function upsertVoucher(
  320.         ?ProjectOrders $projectOrder,
  321.         ?ProjectOrderUndertakings $undertaking,
  322.         ProjectOrderService $projectOrderService,
  323.         ProjectOrderSerialize $projectOrderSerialize,
  324.         TranslatorInterface $translator,
  325.         Request $request,
  326.         int $order_id,
  327.         ?int $voucher_id ): Response
  328.     {
  329.         return $this->json([
  330.             "message" => $translator->trans('This route not more efficient, please use direct in/on Billings Service'),
  331.             "severity" => SeverityInterface::WARNING
  332.         ], Response::HTTP_UNAUTHORIZED );
  333.         if ($order_id && !$projectOrder) {
  334.             return $this->json([
  335.                 "message"  => $translator->trans('Project order not found'),
  336.                 "severity" => SeverityInterface::ERROR
  337.             ], Response::HTTP_NOT_FOUND);
  338.         }
  339.         // matched !!
  340.         if(!$undertaking && $voucher_id){
  341.             #dd($projectOrder);
  342.             $founded $projectOrder->getProjectOrderUndertakings()->exists( fn($keyProjectOrderUndertakings $undertaking) => $undertaking->getId() === $voucher_id );
  343.             if(!$founded){
  344.                 return $this->json([
  345.                     "message"  => $translator->trans('Voucher not matched with order!'),
  346.                     "severity" => SeverityInterface::ERROR
  347.                 ], Response::HTTP_NOT_FOUND);
  348.             }
  349.         }
  350.         $voucher $projectOrderService->upsertVoucher($projectOrder$undertaking$request->request);
  351.         #dd($voucher);
  352.         if($projectOrderService->getException()){
  353.             return $this->json([
  354.                 "message" => $projectOrderService->getException()->getMessage(),
  355.                 "severity" => "warning"
  356.             ], $projectOrderService->getException()->getCode() );
  357.         }
  358.         $serializedVoucher $projectOrderSerialize->setEntity($voucher)->serializeVoucher();
  359.         $serializedMetrics $projectOrderSerialize->setEntity($projectOrder->getProjectOrderMetrics())->serializeFull();
  360.         // Just info
  361.         $serializedOrder $projectOrderSerialize->setEntity($projectOrder)->serializeFull();
  362.         return $this->json([
  363.             "message" => $translator->trans('Loaded project orders successfully'),
  364.             "severity" => SeverityInterface::SUCCESS,
  365.             "voucher" => $serializedVoucher,
  366.             "metrics" => $serializedMetrics,
  367.             "hotEntity" => $serializedOrder,
  368.         ], Response::HTTP_OK );
  369.     }
  370.     /**
  371.      * TODO Remove forever
  372.      * @deprecated
  373.      * @see removeBillings in this controller
  374.      * @Route("/delete-voucher/{voucher_id}", name="api_v3_project_order_delete_voucher", methods={"POST"}, requirements={"voucher_id": "\d+"} )
  375.      * @ParamConverter("undertaking", options={"mapping":{"voucher_id": "id"}})
  376.      * @deprecated
  377.      */
  378.     public function deleteVoucher(?ProjectOrderUndertakings $undertakingProjectOrderService $projectOrderServiceProjectOrderSerialize $projectOrderSerializeTranslatorInterface $translator): Response {
  379.         return $this->json([
  380.             "message" => $translator->trans('This route not more efficient, please use direct in/on Billings Service'),
  381.             "severity" => SeverityInterface::WARNING
  382.         ], Response::HTTP_UNAUTHORIZED );
  383.         if(!$undertaking){
  384.             return $this->json([
  385.                 "message" => $translator->trans("Undertaking not found!"),
  386.                 "severity" => SeverityInterface::ERROR,
  387.             ], Response::HTTP_NOT_FOUND);
  388.         }
  389.         $clonedVoucher = [
  390.             "id" => $undertaking->getId()
  391.         ];
  392.         // Store
  393.         $serializedVoucher $projectOrderSerialize->setEntity($undertaking)->serializeVoucher();
  394.         // Delete -> Metrics has been fresh after delete
  395.         $metrics $projectOrderService->deleteVoucher($undertaking);
  396.         if($projectOrderService->getException()){
  397.             return $this->json([
  398.                 "message" => $projectOrderService->getException()->getMessage(),
  399.                 "severity" => SeverityInterface::WARNING
  400.             ], Response::HTTP_BAD_REQUEST );
  401.         }
  402.         $serializedMetrics $projectOrderSerialize->setEntity($metrics)->serializeFull();
  403.         return $this->json([
  404.             "message" => "Undertaking removed successfully",
  405.             "severity" => SeverityInterface::SUCCESS,
  406.             "id" => $clonedVoucher['id'],
  407.             "voucher" => $serializedVoucher,
  408.             "metrics" => $serializedMetrics,
  409.             "mode" => "delete"
  410.         ]);
  411.     }
  412.     /**
  413.      * @Route("/refresh-metrics/{order_id}", name="api_v3_project_order_refresh_metrics", methods={"POST"}, requirements={"order_id": "\d+"} )
  414.      * @ParamConverter("projectOrder", options={"mapping":{"order_id": "id"}})
  415.      * @throws \Exception
  416.      */
  417.     public function refreshMetrics(?ProjectOrders $projectOrderProjectOrderService $projectOrderServiceProjectOrderSerialize $projectOrderSerializeTranslatorInterface $translatorint $order_id null): Response {
  418.         if(!$projectOrder){
  419.             return $this->json([
  420.                 "message" => $translator->trans("Undertaking not found!"),
  421.                 "severity" => SeverityInterface::ERROR,
  422.             ], Response::HTTP_NOT_FOUND);
  423.         }
  424.         // Void in Service flushed
  425.         // $projectOrderCalculator->updateProjectOrderMetrics($projectOrder);
  426.         $projectOrderService
  427.             ->setEntity($projectOrder$order_id)
  428.             ->recalculateMetrics();
  429.         if($projectOrderService->getException()){
  430.             return $this->json([
  431.                 "message" => $projectOrderService->getException()->getMessage(),
  432.                 "severity" => SeverityInterface::WARNING
  433.             ], Response::HTTP_BAD_REQUEST );
  434.         }
  435.         // Store
  436.         $serializedMetrics $projectOrderSerialize->setEntity($projectOrder->getProjectOrderMetrics())->serializeMetrics();
  437.         return $this->json([
  438.             "message" => "Undertaking removed successfully",
  439.             "severity" => SeverityInterface::SUCCESS,
  440.             "project_order_metrics" => $serializedMetrics,
  441.         ], Response::HTTP_OK);
  442.     }
  443.     /**
  444.      * @Route("/expenses/{order_id}", name="api_v3_project_order_expenses")
  445.      * @ParamConverter("order", options={"mapping": {"order_id": "id"}})
  446.      * @throws \ReflectionException
  447.      * @throws \Exception
  448.      */
  449.     function expenses( ?ProjectOrders $orderProjectOrderService $projectOrderServiceExpenseSerialize $expenseSerializeTranslatorInterface $translator, ?int $order_id null): Response {
  450.         if($order_id && !$order){
  451.             return $this->json([
  452.                 "message" => $translator->trans("Order not found!"),
  453.                 "severity" => SeverityInterface::ERROR,
  454.             ], Response::HTTP_NOT_FOUND);
  455.         }
  456.         $result   $projectOrderService->setEntity($order)->orderExpenses();
  457.         $expenses $result['classic'];
  458.         $stock    $result['stock'];
  459.         $serializedExpenses $expenseSerialize->setEntity($expenses)->serializeOrderExpenses();
  460.         $serializedStock    $expenseSerialize->setEntity($stock)->serializeOrderStocks();
  461.         return $this->json([
  462.             "message" => "Successfully loaded",
  463.             "severity" => "success",
  464.             "expenses" => $serializedExpenses,
  465.             "stock"    => $serializedStock
  466.         ]);
  467.     }
  468.     /**
  469.      * @Route("/labor-details/{order_id}", name="employee_worked_navigator", methods={"POST"})
  470.      * @ParamConverter("order", options={"mapping": {"order_id": "id"}})
  471.      * @throws \Exception
  472.      */
  473.     function laborDetails(?ProjectOrders $orderProjectOrderService $projectOrderServiceRequest $request): Response{
  474.         $laborDetails $projectOrderService
  475.             ->setEntity($order)
  476.             ->setParams($request->request)
  477.             ->laborDetails();
  478.         if($projectOrderService->getException()){
  479.             return $this->json([
  480.                 "message" => $projectOrderService->getException()->getMessage(),
  481.                 "severity" => SeverityInterface::WARNING
  482.             ], Response::HTTP_BAD_REQUEST );
  483.         }
  484.         return $this->json($laborDetailsResponse::HTTP_OK);
  485.     }
  486.     /**
  487.      * @Route("/labor-details-pdf/{order_id}", name="api_v3_project_order_labor_details_pdf", methods={"POST"})
  488.      * @ParamConverter("order", options={"mapping": {"order_id": "id"}})
  489.      */
  490.     public function laborDetailsPdf(
  491.         ?ProjectOrders $order,
  492.         Request $request,
  493.         ProjectOrderService $projectOrderService,
  494.         TranslatorInterface $translator
  495.     ): Response {
  496.         if (!$order) {
  497.             return $this->json([
  498.                 'message'  => $translator->trans('Order not found!'),
  499.                 'severity' => SeverityInterface::WARNING,
  500.             ], Response::HTTP_NOT_FOUND);
  501.         }
  502.         /** @var \App\Entity\User $currentUser */
  503.         $currentUser $this->getUser();
  504.         $printedBy   $currentUser $currentUser->getFullName() : '—';
  505.         $result $projectOrderService
  506.             ->setEntity($order)
  507.             ->setParams($request->request)
  508.             ->laborDetailsPdf($printedBy);
  509.         if ($projectOrderService->getException()) {
  510.             return $this->json([
  511.                 'message'  => $projectOrderService->getException()->getMessage(),
  512.                 'severity' => SeverityInterface::ERROR,
  513.             ], Response::HTTP_BAD_REQUEST);
  514.         }
  515.         return $this->json(
  516.             array_merge($result, ['severity' => SeverityInterface::SUCCESS]),
  517.             Response::HTTP_OK
  518.         );
  519.     }
  520.     /**
  521.      * Silme/tasima oncesi deger-bazli bagimlilik ozeti (preview).
  522.      * Worktime icin tum timesheet'ler taranir -> streamed (progress).
  523.      * EventSource yalnizca GET yapar -> methods YAZILMAZ.
  524.      *
  525.      * @Route("/dependency-summary/{order_id}", name="api_v3_project_order_dependency_summary", requirements={"order_id": "\d+"} )
  526.      * @ParamConverter("order", options={"mapping":{"order_id": "id"}})
  527.      */
  528.     public function dependencySummary(?ProjectOrders $orderProjectOrderService $projectOrderServiceTranslatorInterface $translator): StreamedResponse
  529.     {
  530.         $streaming = new StreamingService([
  531.             "localText" => ["completed" => $translator->trans('Completed')]
  532.         ]);
  533.         $streaming->appendStream(
  534.             'summary',
  535.             $translator->trans('Calculating order dependencies, please wait...'),
  536.             function () use ($order$projectOrderService$translator) {
  537.                 if (!$order) {
  538.                     throw new \DomainException($translator->trans('Order not found'));
  539.                 }
  540.                 return $projectOrderService->setEntity($order)->dependencySummary();
  541.             }
  542.         );
  543.         return $streaming->flush();
  544.     }
  545.     /**
  546.      * Order'i ve butun bagimliliklarini cascade siler.
  547.      * Kesilmis faturaya bagli pozisyon varsa reddeder.
  548.      *
  549.      * @Route("/delete/{order_id}", name="api_v3_project_order_delete", methods={"POST"}, requirements={"order_id": "\d+"} )
  550.      * @ParamConverter("order", options={"mapping":{"order_id": "id"}})
  551.      * @throws \Exception
  552.      */
  553.     public function delete(?ProjectOrders $orderProjectOrderService $projectOrderServiceTranslatorInterface $translatorRequest $request, ?int $order_id null): Response
  554.     {
  555.         if ($order_id && !$order) {
  556.             return $this->json([
  557.                 "message" => $translator->trans("Order not found!"),
  558.                 "severity" => SeverityInterface::ERROR
  559.             ], Response::HTTP_NOT_FOUND);
  560.         }
  561.         // Faturali order'da blok'u asmak icin force + override sifresi
  562.         $force filter_var($request->request->get('force'), FILTER_VALIDATE_BOOLEAN);
  563.         $password $request->request->get('password');
  564.         $result $projectOrderService->setEntity($order)->cascadeDelete($force$password);
  565.         if ($projectOrderService->getException()) {
  566.             $exception $projectOrderService->getException();
  567.             return $this->json([
  568.                 "message" => $exception->getMessage(),
  569.                 "severity" => SeverityInterface::WARNING
  570.             ], $exception->getCode() === Response::HTTP_UNAUTHORIZED Response::HTTP_UNAUTHORIZED Response::HTTP_BAD_REQUEST);
  571.         }
  572.         return $this->json([
  573.             "message" => $translator->trans('Order and its dependencies deleted successfully'),
  574.             "severity" => SeverityInterface::SUCCESS,
  575.             "id" => $result['id'],
  576.             "mode" => "delete",
  577.             "source_project" => $result['source_project'] ?? null
  578.         ], Response::HTTP_OK);
  579.     }
  580.     /**
  581.      * Order'i + TUM bagimliliklarini streamed (adim adim) force-delete eder. GERI DONUS YOK.
  582.      * EventSource yalnizca GET -> methods YAZILMAZ. Sifre query param (fatura bagliysa zorunlu).
  583.      *
  584.      * @Route("/force-delete-streamed/{order_id}", name="api_v3_project_order_force_delete_streamed", requirements={"order_id": "\d+"} )
  585.      * @ParamConverter("order", options={"mapping":{"order_id": "id"}})
  586.      */
  587.     public function forceDeleteStreamed(?ProjectOrders $orderProjectOrderService $projectOrderServiceRequest $request): StreamedResponse
  588.     {
  589.         $password $request->query->get('password');
  590.         return $projectOrderService->setEntity($order)->forceDeleteStreamed($password);
  591.     }
  592.     /**
  593.      * Order'in butun bagimliliklarini hedef order'a tasir (ayni proje icinde).
  594.      *
  595.      * @Route("/move/{from_order_id}/{to_order_id}", name="api_v3_project_order_move", methods={"POST"}, requirements={"from_order_id": "\d+", "to_order_id": "\d+"} )
  596.      * @ParamConverter("from", options={"mapping":{"from_order_id": "id"}})
  597.      * @ParamConverter("to", options={"mapping":{"to_order_id": "id"}})
  598.      * @throws \Exception
  599.      */
  600.     public function move(?ProjectOrders $from, ?ProjectOrders $toProjectOrderService $projectOrderServiceTranslatorInterface $translator): Response
  601.     {
  602.         if (!$from || !$to) {
  603.             return $this->json([
  604.                 "message" => $translator->trans("Order not found!"),
  605.                 "severity" => SeverityInterface::ERROR
  606.             ], Response::HTTP_NOT_FOUND);
  607.         }
  608.         $result $projectOrderService->setEntity($from)->moveAllDependenciesTo($to);
  609.         if ($projectOrderService->getException()) {
  610.             return $this->json([
  611.                 "message" => $projectOrderService->getException()->getMessage(),
  612.                 "severity" => SeverityInterface::WARNING
  613.             ], Response::HTTP_BAD_REQUEST);
  614.         }
  615.         return $this->json([
  616.             "message" => $translator->trans('Order dependencies moved successfully'),
  617.             "severity" => SeverityInterface::SUCCESS,
  618.             "from" => $result['from'],
  619.             "to" => $result['to'],
  620.             "mode" => "move"
  621.         ], Response::HTTP_OK);
  622.     }
  623. }