src/EventSubscriber/DoctrineSubscriber.php line 142

Open in your IDE?
  1. <?php
  2. namespace App\EventSubscriber;
  3. use App\Controller\Api\DocumentManagerController;
  4. use App\Entity\InvoicePaymentStatus;
  5. use App\Entity\InvoicePositions;
  6. use App\Entity\Invoices;
  7. use App\Entity\Partner;
  8. use App\Entity\PartnerCertificateDocuments;
  9. use App\Entity\ProjectOrders;
  10. use App\Entity\ProjectOrderTaskFulfillmentWorkDocs;
  11. use App\Entity\Projects;
  12. use App\Entity\ProjectStakeholders;
  13. use App\Entity\ProjectStatus;
  14. use App\Entity\StockItems;
  15. use App\Entity\Stocks;
  16. use App\Entity\Vendor;
  17. use App\Entity\WorkerActivities;
  18. use App\Entity\WorkerCertificateDocuments;
  19. use App\Entity\Worker;
  20. use App\Entity\WorkerInProject;
  21. use App\Enum\PaymentStatusEnum;
  22. use App\Service\Calculators\ProjectOrderCalculator;
  23. use Doctrine\Common\Collections\ArrayCollection;
  24. use Doctrine\Common\Collections\Criteria;
  25. use Doctrine\ORM\EntityManagerInterface;
  26. use Doctrine\ORM\Event\OnFlushEventArgs;
  27. use Doctrine\ORM\Event\PostFlushEventArgs;
  28. use Doctrine\ORM\Event\PrePersistEventArgs;
  29. use Doctrine\ORM\Event\PreRemoveEventArgs;
  30. use Doctrine\ORM\PersistentCollection;
  31. use Doctrine\Persistence\Event\LifecycleEventArgs;
  32. use Doctrine\Persistence\Event\LifecycleEventArgs as LifecycleEventArgsAlias;
  33. use Doctrine\ORM\Events;
  34. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  35. use App\Entity\InvoiceType;
  36. use Doctrine\Persistence\ManagerRegistry;
  37. use Doctrine\ORM\UnitOfWork;
  38. use Symfony\Contracts\Translation\TranslatorInterface;
  39. /**
  40.  * Doctrine during load jobs
  41. */
  42. class DoctrineSubscriber implements EventSubscriberInterface
  43. {
  44.     private $serviceArgs;
  45.     private ManagerRegistry $managerRegistry;
  46.     private TranslatorInterface $translator;
  47.     private ProjectOrderCalculator $projectOrderCalculator;
  48.     /**
  49.      * @deprecated
  50.     */
  51.     private PaymentStatusEnum $paymentStatusEnum;
  52.     public function __construct(
  53.         $args,
  54.         ManagerRegistry $managerRegistry,
  55.         TranslatorInterface $translator,
  56.         ProjectOrderCalculator $projectOrderCalculator,
  57.         PaymentStatusEnum $paymentStatusEnum
  58.     )
  59.     {
  60.         $this->serviceArgs $args;
  61.         $this->managerRegistry $managerRegistry;
  62.         $this->translator $translator;
  63.         $this->projectOrderCalculator $projectOrderCalculator;
  64.         $this->paymentStatusEnum $paymentStatusEnum;
  65.     }
  66.     // Listen to postLoad event
  67.     public static function getSubscribedEvents()
  68.     {
  69.         /**
  70.          * Metrics hesaplama onFlush
  71.          * Delete öncesi kontrol preRemove
  72.          * DB sonrası işlem (log / event) postFlush
  73.          *
  74.          *  Metrics için önerilmez
  75.          *  preUpdate|postUpdate|postLoad
  76.         */
  77.         return array(
  78.             /**
  79.              * Entity DB’den yüklendikten hemen sonra
  80.             */
  81.             Events::postLoad,
  82.             #Events::postUpdate,
  83.             /**
  84.              * 📍 Ne zaman?
  85.              * persist() çağrıldıktan sonra
  86.              * flush() öncesi
  87.              * Sadece NEW entity için
  88.              * 📍 Ne için?
  89.              * Default value set
  90.              * createdAt / UUID
  91.             */
  92.             Events::prePersist,
  93.             /**
  94.              * 📍 Ne zaman?
  95.              * UPDATE edilecek entity için
  96.              * flush() öncesi
  97.              * 📍 Özel durum:
  98.              * ChangeSet zaten hesaplanmış
  99.              * Değişiklik yaptıysan:
  100.             */
  101.             #Events::preUpdate,
  102.             /**
  103.              * 📍 Ne zaman?
  104.              * remove() çağrıldıktan sonra
  105.              * flush() öncesi
  106.              * 📍 Ne için?
  107.              * Delete öncesi validation
  108.              * Loglama
  109.              * 🚫 Yapma: başka entity update
  110.             */
  111.             Events::preRemove,
  112.             /**
  113.              * 📍 Ne zaman?
  114.              * Doctrine ChangeSet’i hesapladıktan sonra
  115.              * SQL yazılmadan hemen önce
  116.              * 📍 Ne için? 🔥🔥🔥
  117.              * Başka entity’leri manuel olarak update etmek
  118.              * $uow->recomputeSingleEntityChangeSet()
  119.             */
  120.             Events::onFlush,
  121.             /**
  122.              * 📍 Ne zaman?
  123.              * Tüm SQL’ler çalıştıktan sonra
  124.              * Transaction commit edilmeden hemen önce
  125.              * 📍 Ne için?
  126.              * Side effects
  127.              * Queue / mail / log
  128.              * Flush çağırmak SERBEST (dikkatli)
  129.              * 🚫 Yapma: entity state değiştirme (sonsuz loop riski)
  130.             */
  131.             Events::postFlush
  132.         );
  133.     }
  134.     // Call on Update
  135.     public function postFlush(PostFlushEventArgs $args){
  136.     }
  137.     public function preRemove(PreRemoveEventArgs $args){
  138.         /*$manager = $args->getObjectManager();
  139.         $uow = $manager->getUnitOfWork();
  140.         #dd($uow->getScheduledEntityDeletions());
  141.         $entity = $args->getObject();
  142.         #dd($entity);
  143.         // Update Metrics
  144.         if( $entity instanceof ProjectOrderUndertakings){
  145.             $calculated = $this->projectOrderCalculator
  146.                 ->calculateOrderPayments($entity->getProjectOrder());
  147.             #dd($calculated);
  148.             $entity->getProjectOrder()->getProjectOrderMetrics()->setTotalUndertakings($calculated['undertakingTotal'] - $entity->getPrice());
  149.         }*/
  150.     }
  151.     // Call on (New & Update & Remove)
  152.     public function onFlush(OnFlushEventArgs $args)
  153.     {
  154.         /***
  155.          * $args->getObjectManager() → EntityManager döner
  156.          * ❌ Flush edilen entity dönmez
  157.          * ❌ instanceof ProjectOrderUndertakings burada anlamsız
  158.          * Yani şunlar artık BOŞ:
  159.          * getScheduledEntityInsertions()
  160.          * getScheduledEntityUpdates()
  161.          * getScheduledEntityDeletions()
  162.          */
  163.         $manager $args->getObjectManager();
  164.         $uow $manager->getUnitOfWork();
  165.         /**
  166.          * @deprecated
  167.         */
  168.         $deleted array_values($uow->getScheduledEntityDeletions());
  169.         /**
  170.          * ornek entityRemove($entity) busekilde setEntity(null) oldugundan delete yapilmis entity degeri gorunmuyot yani deleted ici bos o yüzden preRemove kullan
  171.         */
  172.         /**
  173.          * $entities = [...$uow->getScheduledEntityUpdates(), ...$uow->getScheduledEntityInsertions()  ];
  174.          * // Trigger's
  175.          * foreach ($entities as $entity) {
  176.          *      if ($entity instanceof ProjectOrderUndertakings) {
  177.          *          * $trigger = new ProjectOrderMetricsTrigger($this->projectOrderCalculator);
  178.          *          * // $trigger->projectOrderUndertakingChange($uow, $manager, $entity, $deleted);
  179.          *          * // Only Remove is Manually With OnRemove in owned Service !!
  180.          *          * // ⚠️Don't use this success over inline ProjectOrderService
  181.          *          * $trigger->projectOrderUndertakingChange($uow, $manager, $entity, []);
  182.          * }
  183.          * }
  184.         */
  185.     }
  186.     // Call on (New)
  187.     public function prePersistPrePersistEventArgs $args)
  188.     {
  189.         $entity $args->getObject();
  190.         if( $entity instanceof ProjectOrderTaskFulfillmentWorkDocs ){
  191.             $entity->setFile($this->serviceArgs['dirs']['taskFulfillmentWorkDocsReadDir'] . DIRECTORY_SEPARATOR $entity->getName() );
  192.         }
  193.     }
  194.     // Set the services.yaml param on the entity
  195.     public function postLoadLifecycleEventArgs $args)
  196.     {
  197.         $entity $args->getObject();
  198.         if ($entity instanceof PartnerCertificateDocuments ) {
  199.             $entity->setHydrateDocumentsData([
  200.                 "id" => $entity->getId(),
  201.                 "file_name" => $entity->getName(),
  202.                 "file_real_path" => $this->serviceArgs['dirs']['partnerCertFetchDir'] . DIRECTORY_SEPARATOR $entity->getName(),
  203.                 "extension" =>  DocumentManagerController::getFileExtension$entity->getName() ),
  204.                 "type" =>  DocumentManagerController::getFileFullTypeDocumentManagerController::getFileExtension$entity->getName() ) ),
  205.                 // Access from frontend
  206.                 "remove_path" => "/api/partner/remove-partner-certificate-history-document/" $entity->getPartnerCertificateHistory()->getPartnerCertificate()->getPartner()->getSlug() . "/" DocumentManagerController::Encrypt($entity->getId()),
  207.                 // Access just backend
  208.                 "remove_link" => $this->serviceArgs['dirs']['partnerCertManageDir'] . DIRECTORY_SEPARATOR $entity->getName()
  209.             ]);
  210.         }
  211.         if ( $entity instanceof WorkerCertificateDocuments ) {
  212.             $entity->setHydrateDocumentsData([
  213.                 "id" => $entity->getId(),
  214.                 "file_name" => $entity->getName(),
  215.                 "file_real_path" => $this->serviceArgs['dirs']['workerCertFetchDir'] . DIRECTORY_SEPARATOR $entity->getName(),
  216.                 "extension" =>  DocumentManagerController::getFileExtension$entity->getName() ),
  217.                 "type" =>  DocumentManagerController::getFileFullTypeDocumentManagerController::getFileExtension$entity->getName() )),
  218.                 // Access from frontend
  219.                 "remove_path" => "/api/worker/remove-worker-certificate-history-document/" $entity->getWorkerCertificateHistory()->getWorkerCertificate()->getWorker()->getSlug() . "/" DocumentManagerController::Encrypt($entity->getId()),
  220.                 // Access just backend
  221.                 "remove_link" =>$this->serviceArgs['dirs']['workerCertManageDir'] . DIRECTORY_SEPARATOR $entity->getName()
  222.             ]);
  223.         }
  224.         if ( $entity instanceof Worker ) {
  225.             #dd(12);
  226.             // Image Full Path
  227.             // $entity->setProfileImageFullPath( $this->serviceArgs['dirs']['workerImagesFetchDir'] . DIRECTORY_SEPARATOR . $entity->getProfileImage() );
  228. //            // SECTION “idempotent” Behevior
  229. //            $profileImage = $entity->getProfileImage();
  230. //            if ($profileImage && !str_starts_with($profileImage, $this->serviceArgs['dirs']['workerImagesFetchDir'])) {
  231. //                $entity->setProfileImage($this->serviceArgs['dirs']['workerImagesFetchDir'] . DIRECTORY_SEPARATOR . $profileImage);
  232. //            }
  233.             /**
  234.              * SECTION Worker için sadece runtime path ekle
  235.              * // README
  236.              * 1️⃣ Doctrine UnitOfWork “dirty check” yapıyor
  237.              * postLoad sırasında sen Worker entity’sinde setProfileImage() cagirdim.
  238.              * Doctrine bunu entity’nin değiştiği olarak algılar ve next flush’ta DB’ye yazılacak olarak işaretledi eget bu entity guncellemsini istemesem bile sirf bu yüzden worker guncellencek .
  239.              * postLoad içinde set yaptığımda, eğer o entity UnitOfWork tarafından izleniyorsa ve flush çağrılırsa, Doctrine bunu kaydediyor benide öldürüyor
  240.              * Gunvenli yol olarak runtime Path eklemeyi yaptim
  241.              * */
  242.             $this->setWorkerImagePathRuntime($entity);
  243.             // $entity->setProfileImage( $this->serviceArgs['dirs']['workerImagesFetchDir'] . DIRECTORY_SEPARATOR . $entity->getProfileImage() );
  244.             #dd($args->getObjectManager()->getMetadataFactory());
  245.             // Latest Activity
  246.             $criteria Criteria::create()->where(Criteria::expr()->eq('exit_at'null));
  247.             // $criteria->orderBy(['created_at' => Criteria::DESC])->setMaxResults(1);
  248.             $criteria->orderBy(['id' => Criteria::DESC])->setMaxResults(1);
  249.             $latestActivity $entity->getWorkerActivities()->matching($criteria)->first();
  250.             $latestActivity $latestActivity instanceof WorkerActivities $latestActivity null;
  251.             // Status
  252.             $status false;
  253.             if ($latestActivity !== null) {
  254.                 $today = new \DateTimeImmutable('now');
  255.                 $exitAt $latestActivity->getExitAt();
  256.                 if ($exitAt) {
  257.                     // Eğer çıkış zamanı gelecekteyse aktif kabul et
  258.                     $status $today $exitAt;
  259.                 } else {
  260.                     // Çıkış zamanı yoksa hâlâ aktif
  261.                     $status true;
  262.                 }
  263.                 $latestActivity->setActivityStatus($status);
  264.                 if($exitAt){
  265.                     dd($exitAt$today->diff($exitAt)->days$status);
  266.                 }
  267.                 // Hesapladığın durumu activity'e ata
  268.             }
  269.             $entity
  270.                 ->setFullName$entity->getName() . ' ' $entity->getSurname() )
  271.                 ->setLatestActivity($latestActivity);
  272.         }
  273.         if ( $entity instanceof Partner ) {
  274.             $entity->setLogoImageFullPath$this->serviceArgs['dirs']['partnerImagesFetchDir'] . DIRECTORY_SEPARATOR $entity->getCompanyLogo() );
  275.             $entity->setCompanyLogo$this->serviceArgs['dirs']['partnerImagesFetchDir'] . DIRECTORY_SEPARATOR $entity->getCompanyLogo() );
  276.         }
  277.         if ( $entity instanceof Vendor ) {
  278.             $entity->setVendorLogo$this->serviceArgs['dirs']['vendorLogosDirectory'] . DIRECTORY_SEPARATOR $entity->getVendorLogo() );
  279.         }
  280.         if( $entity instanceof ProjectOrderTaskFulfillmentWorkDocs ){
  281.             dump($this->serviceArgs['dirs']['taskFulfillmentWorkDocsReadDir'] . DIRECTORY_SEPARATOR $entity->getName());
  282.             $entity->setFile($this->serviceArgs['dirs']['taskFulfillmentWorkDocsReadDir'] . DIRECTORY_SEPARATOR $entity->getName() );
  283.         }
  284.         if ( $entity instanceof WorkerInProject || $entity instanceof ProjectStakeholders  || $entity instanceof Projects ) {
  285.             # 1,completed,success
  286.             # 2,in process,warning
  287.             $completed      false;
  288.             $processId      2;
  289.             $message        $this->translator->trans('in process');
  290.             $projectName    '';
  291.             $alerted   ''// Bitis zamani belli henuz zamani gelmemis 
  292.             if( $entity instanceof Projects ){
  293.                 // Core First of all
  294.                 $project        $entity;
  295.                 $projectEnded   $project->getEndAt() && ($project->getEndAt() < new \DateTimeImmutable('now'));
  296.                 $projectName    $project->getName();
  297.                 // $alerted        = $project->getEndAt() && ($project->getEndAt() > new \DateTimeImmutable('now'));
  298.                 $alerted        = !is_null($project->getEndAt()) ? (new \DateTimeImmutable('now'))->diff($project->getEndAt())->days 'Infinity';
  299.                 if( $projectEnded ){
  300.                     $params "completed";
  301.                     $completed true;
  302.                 }
  303.             }
  304.             elseif( $entity instanceof ProjectStakeholders ){
  305.                 // Core First of all
  306.                 $project        $entity->getProject();
  307.                 $projectEnded   $project->getEndAt() && ($project->getEndAt() < new \DateTimeImmutable('now'));
  308.                 $projectName    $project->getName();
  309.                 // $alerted        = !is_null($project->getEndAt()) && ($project->getEndAt() > new \DateTimeImmutable('now'));
  310.                 $alerted        = !is_null($project->getEndAt()) ? (new \DateTimeImmutable('now'))->diff($project->getEndAt())->days 'Infinity';
  311.                 if($projectEnded){
  312.                     $completed true;
  313.                     $message $this->translator->trans('completed');
  314.                     $processId 1;
  315.                 } else {
  316.                     // 2. Stakeholder
  317.                     $project        $entity;
  318.                     $projectEnded   $project->getEndAt() && ($project->getEndAt() < new \DateTimeImmutable('now'));
  319.                     // $alerted        = !is_null($project->getEndAt()) && ($project->getEndAt() > new \DateTimeImmutable('now'));
  320.                     $alerted        = !is_null($project->getEndAt()) ? (new \DateTimeImmutable('now'))->diff($project->getEndAt())->days 'Infinity';
  321.                     if($projectEnded){
  322.                         $completed true;
  323.                         $message $this->translator->trans('completed');
  324.                         $processId 1;
  325.                     }
  326.                 }
  327.             }
  328.             elseif( $entity instanceof WorkerInProject ){
  329.                 // Core First of all
  330.                 $project        $entity->getProject()->getProject();
  331.                 $alerted        'Infinity';
  332.                 $projectEnded   false;
  333.                 if(!is_null($project->getEndAt())){
  334.                     $diff           = (new \DateTimeImmutable('now'))->diff($project->getEndAt());
  335.                     $days           $diff->days;
  336.                     $invert         $diff->invert;
  337.                     $projectEnded   $project->getEndAt() < new \DateTimeImmutable('now');
  338.                     $alerted        $invert "-$days$days;
  339.                 }
  340.                 $projectName    $project->getName();
  341.                 if($projectEnded){
  342.                     $completed true;
  343.                     $message $this->translator->trans('completed');
  344.                     $processId 1;
  345.                 } else {
  346.                     // 2. Stakeholder
  347.                     $project        $entity->getProject();
  348.                     $alerted        'Infinity';
  349.                     $projectEnded   false;
  350.                     if(!is_null($project->getEndAt())){
  351.                         $diff           = (new \DateTimeImmutable('now'))->diff($project->getEndAt());
  352.                         $days           $diff->days;
  353.                         $invert         $diff->invert;
  354.                         $projectEnded   $project->getEndAt() < new \DateTimeImmutable('now');
  355.                         $alerted        $invert "-$days$days;
  356.                     }
  357.                     $projectName    $project->getProject()->getName();
  358.                     if($projectEnded){
  359.                         $completed true;
  360.                         $message $this->translator->trans('completed');
  361.                         $processId 1;
  362.                     } else {
  363.                         $project        $entity;
  364.                         $alerted        'Infinity';
  365.                         $projectEnded   false;
  366.                         if(!is_null($project->getEndAt())){
  367.                             $diff           = (new \DateTimeImmutable('now'))->diff($project->getEndAt());
  368.                             $days           $diff->days;
  369.                             $invert         $diff->invert;
  370.                             $projectEnded   $project->getEndAt() < new \DateTimeImmutable('now');
  371.                             $alerted        $invert "-$days$days;
  372.                         }
  373.                         $projectName    $project->getProject()->getProject()->getName();
  374.                         if($projectEnded){
  375.                             $completed true;
  376.                             $message $this->translator->trans('completed');
  377.                             $processId 1;
  378.                         }
  379.                     }
  380.                 }
  381.                 // TODO bu kisim gecici worker_in_project deki project prop u stakeholder_project prop u ile degisecek
  382.                 $entity->setStakeholderProject($entity->getProject());
  383.             }
  384.             try{
  385.                 #dump($message);
  386.                 /**@var $found ProjectStatus*/
  387.                 $found $args->getObjectManager()->getRepository(ProjectStatus::class)->find($processId); // ->findOneBy(["name"=>$message]);
  388.                if(!is_null($found)){
  389.                     $entity->setStatus([
  390.                         "project_name" => $projectName,
  391.                         'name' => $message,
  392.                         'color' => $found->getColor(),
  393.                         'completed' => $completed,
  394.                         'alerted' => $alerted,
  395.                         'show_and_notify' => $alerted !== 'Infinity' && $alerted && $alerted 10,
  396.                         'notify' => is_numeric($alerted) && $alerted $alerted ' ' $this->translator->trans('day(s) to end') : 'Infinity'
  397.                     ]);
  398.                }
  399.                else {
  400.                    dd("Null by ProjectStatus [$message]");
  401.                }
  402.             } catch (\Exception $exception ){
  403.                 throw new \Error("Error by ProjectStatus [$message]");
  404.             }
  405.         }
  406.         /**
  407.          * By Manage give an error 
  408.          * @deprecated */
  409.         if( $entity instanceof ProjectOrders ){
  410.         
  411.             return;
  412.             #dd($args->getObjectManager());
  413.             
  414.             $entityManager $args->getObjectManager();
  415.             
  416.             $unitOfWork $entityManager->getUnitOfWork();
  417.             $entityState $unitOfWork->getEntityState($entity);
  418.             
  419.             if($entityState === UnitOfWork::STATE_NEW)  {
  420.                 // return;
  421.             }
  422.             #return;
  423.             try{
  424.                 if( is_null($entity->getInvoiceType())){
  425.                 
  426.                 
  427.                     #dd(12);
  428.                     $projectOwner $entity->getProject()->getOwner();
  429.     
  430.     
  431.                     // Set Standard
  432.                     if(!$projectOwner){
  433.                         $invoiceTypes $args->getObjectManager()->getRepository(InvoiceType::class)->findOneBy(["name"=>"Standard"]);
  434.                         #dd($invoiceTypes);
  435.                         $entity->setInvoiceType($invoiceTypes);
  436.                         return;
  437.                     }
  438.     
  439.                     // Owner Defined but invoice type not selected than Set Standard 
  440.                     if(is_null($projectOwner->getInvoiceType())){
  441.                         $invoiceTypes $args->getObjectManager()->getRepository(InvoiceType::class)->findOneBy(["name"=>"Standard"]);
  442.                         #dd($invoiceTypes);
  443.                         #$entity->setInvoiceType($invoiceTypes);
  444.                         return;
  445.                     }
  446.     
  447.                     // dd($projectOwner->getInvoiceType());
  448.                     $entity->setInvoiceType($projectOwner->getInvoiceType());
  449.                 }
  450.             } catch (\Exception $exception){
  451.                 
  452.             }
  453.         
  454.         }
  455.         if( $entity instanceof Stocks ){
  456.             $em $args->getObjectManager();
  457.             $availableStockQuantity $this->finalCalculatedStockQuantityFromTransactions($entity->getStockTransactions());
  458.             $entity->setAvailableStockQuantity$availableStockQuantity );
  459.         }
  460.         if( $entity instanceof StockItems ){
  461.             $em $args->getObjectManager();
  462.             $finalQuantities $entity->getStocks()->map(function (/**@var Stocks $stock*/$stock) {
  463.                 return $this->finalCalculatedStockQuantityFromTransactions($stock->getStockTransactions());
  464.             });
  465.             $entity->setTotalAvailableQuantity(array_sum($finalQuantities->toArray()));
  466.         }
  467.         // TODO move this to static as Metric into Invoice Table
  468.         if($entity instanceof Invoices){
  469.             //            $calculated = $approved = $paid = [];
  470.             //
  471.             //            /**@var $invoicePosition InvoicePositions */
  472.             //            foreach ($entity->getInvoicePositions() as $invoicePosition) {
  473.             //                // This position absolute from Billing generated
  474.             //                $price  = $invoicePosition->getOrderBilling()->getPrice();
  475.             //                $unit   = $invoicePosition->getOrderBilling()->getQuantity();
  476.             //                // Required
  477.             //                $calculated[] = $price * $unit;
  478.             //                // Optional
  479.             //                if(!is_null($entity->getApprovedAt())) {
  480.             //                    $approved[] = $entity->getApprovedAmount();
  481.             //                }
  482.             //                // Optional
  483.             //                if(!is_null($entity->getPaidAt())){
  484.             //                    $paid[] = $invoicePosition->getPaidAmount();
  485.             //                }
  486.             //            }
  487.             //
  488.             //            // After each
  489.             //            $calculatedTotals   = array_sum($calculated);
  490.             //            $paidTotals         = array_sum($paid);
  491.             //            $approvedTotals     = array_sum($approved);
  492.             //            $paidStatus         = $this->paymentStatusEnum::PAYMENT_STATUS_UNPAID;
  493.             //            if($paidTotals > 0){
  494.             //                // TODO Paid
  495.             //                if( $paidTotals !== $calculatedTotals ){
  496.             //                    // TODO Partially Paid
  497.             //                    $paidStatus = $this->paymentStatusEnum::PAYMENT_STATUS_PARTIALLY_PAID;
  498.             //                } else {
  499.             //                    // TODO Fully Paid
  500.             //                    $paidStatus = $this->paymentStatusEnum::PAYMENT_STATUS_PAID;
  501.             //                }
  502.             //            }
  503.             //
  504.             //            $entity->setPaidStatus([
  505.             //                "calculated" => $calculatedTotals, "paid" => $paidTotals, "status" => $paidStatus
  506.             //            ]);
  507.         }
  508.         // Update Virtual Column
  509.         if($entity instanceof Invoices){
  510.             if(!is_null($entity->getPaidAt())){
  511.                 $entity->setWeekOfYear($entity->getPaidAt()->format('o-W'));
  512.             }
  513.         }
  514.     }
  515.     private function finalCalculatedStockQuantityFromTransactions(PersistentCollection $transactions): float {
  516.         $finalQuantity 0;
  517.         foreach ( $transactions as $transaction) {
  518.             $qty $transaction->getQuantity();
  519.             if ($transaction->getTransactionType() === 'in') {
  520.                 $finalQuantity += $qty;
  521.             } elseif ($transaction->getTransactionType() === 'out') {
  522.                 $finalQuantity -= $qty;
  523.             }
  524.         }
  525.         return $finalQuantity;
  526.     }
  527.     private function setWorkerImagePathRuntime(Worker $worker): void
  528.     {
  529.         $profileImage $worker->getProfileImage();
  530.         if (!$profileImage) {
  531.             return;
  532.         }
  533.         // 1️⃣ Eğer zaten path eklenmişse tekrar ekleme
  534.         $fetchDir rtrim($this->serviceArgs['dirs']['workerImagesFetchDir'], DIRECTORY_SEPARATOR);
  535.         if (!str_starts_with($profileImage$fetchDir)) {
  536.             // 2️⃣ Runtime olarak path’i set et
  537.             // Reflection ile Doctrine dirty check’i bypass et
  538.             $reflection = new \ReflectionProperty(Worker::class, 'profile_image');
  539.             $reflection->setAccessible(true);
  540.             $reflection->setValue($worker$fetchDir DIRECTORY_SEPARATOR $profileImage);
  541. //            $reflectionFull = new \ReflectionProperty(Worker::class, 'profileImageFullPath');
  542. //            $reflectionFull->setAccessible(true);
  543. //            $reflectionFull->setValue($worker, $fetchDir . DIRECTORY_SEPARATOR . $profileImage);
  544.         }
  545.     }
  546. }