src/Controller/AssetController.php line 525

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Asset;
  4. use App\Entity\AssetLifecyclePeriod;
  5. use App\Entity\AssetMaintenance;
  6. use App\Entity\AssetTransaction;
  7. use App\Entity\Customer;
  8. use App\Entity\Note;
  9. use App\Entity\Repository\AssetRepository;
  10. use App\Entity\Repository\CustomerRepository;
  11. use App\Entity\Repository\TruckAttachmentRepository;
  12. use App\Entity\Repository\TruckChargerRepository;
  13. use App\Entity\Repository\TruckRepository;
  14. use App\Entity\Truck;
  15. use App\Entity\TruckAttachment;
  16. use App\Entity\TruckCharger;
  17. use App\Entity\TruckMake;
  18. use App\Entity\TruckModel;
  19. use App\Form\Type\AssetLifecyclePeriodType;
  20. use App\Form\Type\AssetMaintenanceType;
  21. use App\Form\Type\EndAssetLifecyclePeriodType;
  22. use App\Form\Type\NoteType;
  23. use App\Form\Type\TruckAttachmentType;
  24. use App\Form\Type\TruckChargerType;
  25. use App\Form\Type\TruckModelType;
  26. use App\Form\Type\TruckType;
  27. use App\Model\TransactionCategory;
  28. use Doctrine\Common\Collections\ArrayCollection;
  29. use Doctrine\ORM\EntityManagerInterface;
  30. use Symfony\Component\HttpFoundation\JsonResponse;
  31. use Symfony\Component\HttpFoundation\RedirectResponse;
  32. use Symfony\Component\HttpFoundation\Request;
  33. use Symfony\Component\HttpFoundation\Response;
  34. use Symfony\Component\Routing\Annotation\Route;
  35. class AssetController extends Controller
  36. {
  37.     public function view(?string $idAssetRepository $assetRepository): Response
  38.     {
  39.         $this->securityCheck(['ROLE_SERVICE_ADMIN''ROLE_ENGINEER']);
  40.         if (!$id) {
  41.             throw $this->createNotFoundException('No ID supplied.');
  42.         }
  43.         $asset $assetRepository->find($id);
  44.         if (!$asset) {
  45.             throw $this->createNotFoundException('Asset with ID ' $id ' not found.');
  46.         }
  47.         return $this->render('Asset/view.html.twig', ['asset' => $asset]);
  48.     }
  49.     public function listActive($typeTruckAttachmentRepository $attachmentRepositoryTruckChargerRepository $truckChargerRepositoryTruckRepository $truckRepository$page 1): Response
  50.     {
  51.         $this->securityCheck(['ROLE_SERVICE_ADMIN''ROLE_ENGINEER']);
  52.         $assets = match ($type) {
  53.             'attachment' => $attachmentRepository->getActiveListingsWithArchived(),
  54.             'charger' => $truckChargerRepository->getActiveListingsWithArchived(),
  55.             default => $truckRepository->getActiveListingsWithArchived(),
  56.         };
  57.         return $this->render('Asset/listActive.html.twig', ['assets' => $assets'type'   => $type]);
  58.     }
  59.     public function listForSaleJob(TruckAttachmentRepository $attachmentRepositoryTruckChargerRepository $truckChargerRepositoryTruckRepository $truckRepository): Response
  60.     {
  61.         $this->securityCheck(['ROLE_SERVICE_ADMIN''ROLE_ENGINEER']);
  62.         $assets = [...$attachmentRepository->getUnassignedListings(), ...$truckChargerRepository->getUnassignedListings(), ...$truckRepository->getUnassignedListings()];
  63.         return $this->render('Asset/listForSaleJob.html.twig', ['assets' => $assets]);
  64.     }
  65.     public function getContactCustomer(int $idCustomerRepository $customerRepository): JsonResponse
  66.     {
  67.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  68.         $response = new JsonResponse();
  69.         $customer $customerRepository->find($id);
  70.         if (!$customer) {
  71.             return $response;
  72.         }
  73.         $responseArray = [];
  74.         foreach ($customer->getContacts()->toArray() as $contact) {
  75.             $responseArray[] = ['id' => $contact->getId(), 'name' => $contact->getName()];
  76.         }
  77.         return new JsonResponse($responseArray);
  78.     }
  79.     public function newTruck(Request $requestEntityManagerInterface $em): Response
  80.     {
  81.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  82.         $response = new JsonResponse();
  83.         $truck    = new Truck();
  84.         $showCustomer $request->query->get('showCustomer') ?? false;
  85.         $form     $this->createForm(TruckType::class, $truck, ['showCustomer' => (bool) $showCustomer]);
  86.         $form->handleRequest($request);
  87.         if ($form->isSubmitted() && $form->isValid()) {
  88.             $truck      $form->getData();
  89.             $newMake    $form['newMake']->getData();
  90.             $newModel   $form['newModel']->getData();
  91.             $customer   null;
  92.             $truckMake  null;
  93.             $truckModel null;
  94.             $noteDetail $form['detail']->getData();
  95.             if ($newMake !== null) {
  96.                 $truckMake = new TruckMake();
  97.                 $truckMake->setName($newMake);
  98.                 $em->persist($truckMake);
  99.             }
  100.             if (!empty($newModel)) {
  101.                 $truckModel = new TruckModel();
  102.                 $truckModel->setName($newModel);
  103.                 if ($truckMake) {
  104.                     $truckModel->setMake($truckMake);
  105.                 } else {
  106.                     $truckModel->setMake($form['make']->getData());
  107.                 }
  108.                 $em->persist($truckModel);
  109.                 $truck->setModel($truckModel);
  110.             }
  111.             if ($noteDetail) {
  112.                 $note = new Note();
  113.                 $hr   date('H');
  114.                 $min  date('i');
  115.                 $sec  date('s');
  116.                 $date $form['date']->getData();
  117.                 $date->setTime($hr$min$sec);
  118.                 $note->setDate($date);
  119.                 $note->setAsset($truck);
  120.                 $note->setDetail($form['detail']->getData());
  121.                 $em->persist($note);
  122.             }
  123.             if ($showCustomer) {
  124.                 $customer $form['customer']->getData();
  125.                 $clientFleet $form['clientFleet']->getData();
  126.             } else {
  127.                 $customer $this->getRepo(Customer::class)->getDefaultCustomer();
  128.                 $clientFleet null;
  129.             }
  130.             $period = new AssetLifecyclePeriod($truck);
  131.             $period->setStart(new \DateTime());
  132.                                 
  133.             if ($customer) {
  134.                 $period->setCustomer($customer);
  135.                 $period->setClientFleet($clientFleet);
  136.                 $period->setType(AssetLifecyclePeriod::PERIOD_TYPE_CUSTOMER_OWNED);
  137.                 $contacts = isset($form['contacts']) ? $form['contacts']->getData() : [];
  138.                 foreach ($contacts as $contact) {
  139.                     $contact->addAssetLifecyclePeriod($period);
  140.                     $em->persist($contact);
  141.                     $period->addContact($contact);
  142.                 }
  143.             }
  144.   
  145.             $em->persist($period);
  146.             $em->flush();
  147.             $truck->setCurrentLifecyclePeriod($period);
  148.             $this->getAssetManager()->saveAsset($truck);
  149.             $assign $request->query->get('assign'false);
  150.             if ($truckModel) {
  151.                 $modelForm  $this->createForm(TruckModelType::class, $truckModel);
  152.                 $isElectric $truckModel->getFuel() === 'Electric';
  153.                 $routeParams = ['id' => $truck->getId()];
  154.                 if ($assign) {
  155.                     $routeParams['assign'] = 1;
  156.                 }
  157.                 $response->setData(['success'     => true'displayForm' => true'refresh'     => false'html'        => $this->renderView('Truck/_newModelForm.html.twig', ['form'       => $modelForm->createView(), 'formRoute'  => $this->generateUrl('edit_truck_model'$routeParams), 'truckModel' => $truckModel'isElectric' => $isElectric'assign'     => $assign])]);
  158.                 return $response;
  159.             }
  160.             $this->setFlash('success''The truck with ID "' $truck->getId() . '" has been successfully added');
  161.             if ($assign) {
  162.                 return $this->redirect($this->generateUrl('transfer_asset', ['id' => $truck->getId()]));
  163.             }
  164.             if ($customer) {
  165.                 $response->setData(['success'     => true'displayForm' => false'refresh'     => false'url'         => $this->generateUrl('active_assets') . '?selectedItem=' $truck->getId()]);
  166.             } else {
  167.                 $response->setData(['success'     => true'displayForm' => false'refresh'     => true]);
  168.             }
  169.             return $response;
  170.         }
  171.         $response->setData(['success'      => true'displayForm'  => true'refresh'      => false'showCustomer' => $showCustomer'isEdit'      => false'html'         => $this->renderView('Truck/_form.html.twig', ['form'      => $form->createView(), 'formRoute' => $this->generateUrl('new_truck'), 'truck'     => $truck'isEdit'      => false'period'   => $truck->getCurrentLifecyclePeriod()])]);
  172.         return $response;
  173.     }
  174.     public function editTruck(Request $requestEntityManagerInterface $em$id null): JsonResponse
  175.     {
  176.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  177.         $response = new JsonResponse();
  178.         $truck    $this->getRepo(Truck::class)->find($id);
  179.         if (!$truck) {
  180.             $response->setData([
  181.                 'success'     => false
  182.                 'displayForm' => false
  183.                 'refresh'     => false
  184.                 'message'     => 'The truck was not found.'
  185.             ]);
  186.             return $response;
  187.         }
  188.         $storedContacts = new ArrayCollection();
  189.         foreach ($truck->getContacts() ?? [] as $contact) {
  190.             $storedContacts->add($contact);
  191.         }
  192.         $storedImages = new ArrayCollection();
  193.         foreach ($truck->getImages() as $image) {
  194.             $storedImages->add($image);
  195.         }
  196.         $form $this->createForm(TruckType::class, $truck, [
  197.             'showContacts' => $truck->getPurchaseOrder() == null || $truck->getPurchaseOrder()->getCompleted()
  198.         ]);
  199.         $form->handleRequest($request);
  200.         if ($form->isSubmitted() && $form->isValid()) {
  201.             $truck      $form->getData();
  202.             $newMake    $form['newMake']->getData();
  203.             $newModel   $form['newModel']->getData();
  204.             $truckMake  null;
  205.             $truckModel null;
  206.             foreach ($storedContacts as $storedContact) {
  207.                 if ($truck->getContacts()->contains($storedContact) === false) {
  208.                     $storedContact->removeTruck($truck);
  209.                     $em->persist($storedContact);
  210.                 }
  211.             }
  212.             if ($newMake !== null) {
  213.                 $truckMake = new TruckMake();
  214.                 $truckMake->setName($newMake);
  215.                 $em->persist($truckMake);
  216.             }
  217.             if (!empty($newModel)) {
  218.                 $truckModel = new TruckModel();
  219.                 $truckModel->setName($newModel);
  220.                 $truckModel->setFuel($form['type']->getData());
  221.                 if ($truckMake) {
  222.                     $truckModel->setMake($truckMake);
  223.                 } else {
  224.                     $truckModel->setMake($form['make']->getData());
  225.                 }
  226.                 $em->persist($truckModel);
  227.                 $truck->setModel($truckModel);
  228.             }
  229.             $period $truck->getCurrentLifecyclePeriod();
  230.             // The contacts field is unmapped (mapped => false) so we must manually sync it
  231.             // Since Contact is the owning side of the ManyToMany, we must persist Contact entities
  232.             // to ensure Doctrine updates the join table when contacts are added or removed
  233.             if (isset($form['contacts']) && $period) {
  234.                 // Get the new collection of contacts from the form
  235.                 $newContacts = new ArrayCollection($form['contacts']->getData() ?: []);
  236.                 // Use setContacts() which properly synchronizes both sides of the relationship
  237.                 // This will call removeContact() for removed contacts and addContact() for new ones
  238.                 $period->setContacts($newContacts);
  239.                 // Persist all Contact entities involved (both added and remaining)
  240.                 foreach ($period->getContacts() as $contact) {
  241.                     $em->persist($contact);
  242.                 }
  243.                 // Also persist Contact entities that were removed to ensure join table cleanup
  244.                 $originalContacts $storedContacts;  // Stored before form submission
  245.                 foreach ($originalContacts as $originalContact) {
  246.                     if (!$period->getContacts()->contains($originalContact)) {
  247.                         $em->persist($originalContact);
  248.                     }
  249.                 }
  250.             }
  251.             if (($purchaseOrder $truck->getPurchaseOrder()) && isset($form['leadTime'])) {
  252.                 $purchaseOrder->setLeadTime($form['leadTime']->getData());
  253.                 $purchaseOrder->setEta($form['eta']->getData());
  254.             }
  255.             
  256.             if ($period) {
  257.                 $period->setSalePrice(null ?? $form['stockValue']->getData());
  258.                 $em->persist($period);
  259.             }
  260.                 
  261.             $em->flush();
  262.             // Clear the entity manager to ensure subsequent queries fetch fresh data
  263.             // This is critical for email notifications to use the updated contact list
  264.             $em->clear(AssetLifecyclePeriod::class);
  265.             $em->clear(Contact::class);
  266.             $this->getAssetManager()->saveAsset($truck);
  267.             $response->setData(['success'     => true'displayForm' => false'message'     => 'The truck with ID "' $truck->getId() . '" was successfully edited.''refresh'     => true]);
  268.             $this->setFlash('success''The truck with ID "' $truck->getId() . '" has been successfully edited');
  269.             return $response;
  270.         }
  271.         $response->setData([
  272.             'success'     => true
  273.             'displayForm' => true
  274.             'message'     => null
  275.             'isEdit'      => true
  276.             'refresh'     => false
  277.             'html'        => $this->renderView('Truck/_form.html.twig', [
  278.                 'form'      => $form->createView(), 
  279.                 'period'     => $truck->getCurrentLifecyclePeriod(), 
  280.                 'customerTruck' => $truck->getCustomer(), 
  281.                 'modelForm' => null
  282.                 'formRoute' => $this->generateUrl('edit_truck', ['id' => $id]), 
  283.                 'truck'     => $truck
  284.                 'isEdit'    => true
  285.             ])
  286.         ]);
  287.         return $response;
  288.     }
  289.     public function particulars(Request $requestEntityManagerInterface $em$id null$lifecyclePeriodId null): Response
  290.     {
  291.         $this->securityCheck(['ROLE_SERVICE_ADMIN''ROLE_ENGINEER']);
  292.         $asset $this->getRepo(Asset::class)->find($id);
  293.         $attachedToTruck null;
  294.         if ($asset->getAssetType() === 'Attachment') {
  295.             $attachedToTruck $this->getRepo(Truck::class)->findOneBy(['attachment' => $asset->getId()]);
  296.         } elseif ($asset->getAssetType() === 'Charger') {
  297.             $attachedToTruck $this->getRepo(Truck::class)->findOneBy(['charger' => $asset->getId()]);
  298.         }
  299.         if (!$asset->getCurrentLifecyclePeriod()) {
  300.             $this->getAssetManager()->ensureCurrentLifecyclePeriod($asset);
  301.             $em->flush();
  302.         }
  303.         $period $asset->getCurrentLifecyclePeriod();
  304.         if ($lifecyclePeriodId) {
  305.             $specificPeriod $this->getRepo(AssetLifecyclePeriod::class)->findOneBy(['id'    => $lifecyclePeriodId'asset' => $asset]);
  306.             if ($specificPeriod) {
  307.                 $period $specificPeriod;
  308.             }
  309.         }
  310.         $versions $this->getVersionRepo()->getLogEntries($asset);
  311.         $assetType $asset->getAssetType(true);
  312.         $canEdit $this->isGranted('ROLE_SERVICE_ADMIN') && $request->query->get('disallowEdit'false) === false;
  313.         return $this->render(
  314.             '' $assetType '/particulars.html.twig',
  315.             [
  316.                 'asset' => $asset,
  317.                 'canEdit' => $canEdit,
  318.                 'attachedToTruck' => $attachedToTruck,
  319.                 'versions' => $versions,
  320.                 'period' => $period
  321.             ]);
  322.     }
  323.     public function delete(EntityManagerInterface $em$id null): RedirectResponse
  324.     {
  325.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  326.         $asset $this->getRepo(Asset::class)->find($id);
  327.         if (!$asset) {
  328.             throw $this->createNotFoundException('The asset with ID"' $id '" was not found.');
  329.         }
  330.         $period $asset->getCurrentLifecyclePeriod();
  331.         $hasCustomer $period ? (bool) $period->getCustomer() : false;
  332.         $em->remove($asset);
  333.         $em->flush();
  334.         $this->setFlash('success''Asset "' $asset->getId() . '" was successfully deleted');
  335.         return $this->redirect($this->generateUrl($hasCustomer 'active_assets' 'asset_register'));
  336.     }
  337.     public function getTruckModels($make null): JsonResponse
  338.     {
  339.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  340.         if ($make === null) {
  341.             throw $this->createNotFoundException('No truck make supplied.');
  342.         }
  343.         $truckModels $this->getRepo(TruckModel::class)->getModelsForMake($make);
  344.         $models = [];
  345.         foreach ($truckModels as $truckModel) {
  346.             $models[] = ['id' => $truckModel->getId(), 'name' => $truckModel->getName(), 'fuel' => $truckModel->getFuel()];
  347.         }
  348.         $response = new JsonResponse();
  349.         return $response->setData(['models' => $models]);
  350.     }
  351.     public function editTruckModel(Request $requestEntityManagerInterface $em$id null): Response
  352.     {
  353.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  354.         if ($id === null) {
  355.             throw $this->createNotFoundException('No truck model supplied.');
  356.         }
  357.         $truck      $this->getRepo(Truck::class)->find($id);
  358.         $truckModel $truck->getModel();
  359.         $response   = new JsonResponse();
  360.         $form       $this->createForm(TruckModelType::class, $truckModel);
  361.         $assign $request->query->get('assign'false);
  362.         $form->handleRequest($request);
  363.         if ($form->isSubmitted() && $form->isValid()) {
  364.             $truckModel $form->getData();
  365.             $em->persist($truckModel);
  366.             $em->flush();
  367.             $response->setData(['success'     => true'displayForm' => false'message'     => 'The truck model was successfully updated.''refresh'     => true]);
  368.             $this->setFlash('success''The truck model has been successfully updated');
  369.             if ($assign) {
  370.                 return $this->redirect($this->generateUrl('transfer_asset', ['id' => $id]));
  371.             }
  372.             return $response;
  373.         }
  374.         $routeParams = ['id' => $id];
  375.         if ($assign) {
  376.             $routeParams['assign'] = 1;
  377.         }
  378.         $response->setData(['success'     => false'displayForm' => true'message'     => null'isEdit'      => true'refresh'     => false'html'        => $this->renderView('Truck/_newModelForm.html.twig', ['form'       => $form->createView(), 'formRoute'  => $this->generateUrl('edit_truck_model'$routeParams), 'truckModel' => $truckModel'isEdit'     => true])]);
  379.         return $response;
  380.     }
  381.     public function listRegister($page 1): Response
  382.     {
  383.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  384.         $trucks      $this->getRepo(Truck::class)->getAllForRegister();
  385.         $attachments $this->getRepo(TruckAttachment::class)->getAllForRegister();
  386.         $chargers    $this->getRepo(TruckCharger::class)->getAllForRegister();
  387.         return $this->render('Asset/listRegister.html.twig', [
  388.             'trucks'      => $trucks
  389.             'attachments' => $attachments,
  390.             'chargers'    => $chargers
  391.         ]);
  392.     }
  393.     public function getAddressForAsset($id null): JsonResponse
  394.     {
  395.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  396.         $response = new JsonResponse();
  397.         $assetPeriod $this->getRepo(AssetLifecyclePeriod::class)->find($id);
  398.         if (!$assetPeriod) {
  399.             throw $this->createNotFoundException('No asset lifecycle period with ID "' $id '" was found.');
  400.         }
  401.         $asset $assetPeriod->getAsset();
  402.         $siteAddress $asset->getSiteAddress();
  403.         $response->setData(['addressId' => ($siteAddress $siteAddress->getId() : null)]);
  404.         return $response;
  405.     }
  406.     public function getAssetInfo($id null\App\Manager\ServiceChecklistManager $checklistManager): JsonResponse
  407.     {
  408.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  409.         $response = new JsonResponse();
  410.         $assetPeriod $this->getRepo(AssetLifecyclePeriod::class)->find($id);
  411.         if (!$assetPeriod) {
  412.             throw $this->createNotFoundException('No asset lifecycle period with ID "' $id '" was found.');
  413.         }
  414.         $levelCAvailable $checklistManager->isLevelCAvailable($assetPeriod);
  415.         $response->setData(['levelCAvailable' => $levelCAvailable]);
  416.         return $response;
  417.     }
  418.     public function getContactsForAsset($id null): JsonResponse
  419.     {
  420.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  421.         $response = new JsonResponse();
  422.         $assetPeriod $this->getRepo(AssetLifecyclePeriod::class)->find($id);
  423.         if (!$assetPeriod) {
  424.             throw $this->createNotFoundException('No asset lifecycle period with ID "' $id '" was found.');
  425.         }
  426.         $contacts $assetPeriod->getContacts() ? $assetPeriod->getContacts()->toArray() : [];
  427.         $contactIds array_map(fn ($contact) => $contact->getId(), $contacts);
  428.         $response->setData(['contacts' => $contactIds]);
  429.         return $response;
  430.     }
  431.     public function newNote(Request $requestEntityManagerInterface $em$id null): JsonResponse
  432.     {
  433.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  434.         if ($id === null) {
  435.             throw $this->createNotFoundException('No asset ID was supplied');
  436.         }
  437.         $asset $this->getRepo(Asset::class)->find($id);
  438.         if (!$asset) {
  439.             throw $this->createNotFoundException('The asset with ID "' $id '" was not found.');
  440.         }
  441.         $response = new JsonResponse();
  442.         $note     = new Note();
  443.         $form $this->createForm(NoteType::class, $note, [
  444.             'includeReason' => false,
  445.         ]);
  446.         $form->handleRequest($request);
  447.         if ($form->isSubmitted() && $form->isValid()) {
  448.             $note $form->getData();
  449.             $this->getAssetManager()->ensureCurrentLifecyclePeriod($asset);
  450.             if ($asset->getCustomer()) {
  451.                 $note->setAssetLifecyclePeriod($asset->getCurrentLifecyclePeriod());
  452.             } else {
  453.                 $note->setAsset($asset);
  454.             }
  455.             $hr   date('H');
  456.             $min  date('i');
  457.             $sec  date('s');
  458.             $date $note->getDate();
  459.             $date->setTime($hr$min$sec);
  460.             $note->setDate($date);
  461.             $em->persist($note);
  462.             $em->flush();
  463.             $response->setData(['success'     => true'displayForm' => false'refresh'     => true]);
  464.             $this->setFlash('success''The note "' $note->getId() . '" has been successfully created for asset "' $asset->getNameString() . '".');
  465.             return $response;
  466.         }
  467.         $response->setData(['success'     => false'displayForm' => true'refresh'     => false'isEdit'      => false'html'        => $this->renderView('Asset/_newNoteForm.html.twig', ['form'      => $form->createView(), 'formRoute' => $this->generateUrl('new_asset_note', ['id' => $id])])]);
  468.         return $response;
  469.     }
  470.     public function newMaintenance(Request $requestEntityManagerInterface $em$id null): JsonResponse
  471.     {
  472.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  473.         if ($id === null) {
  474.             throw $this->createNotFoundException('No asset ID was supplied');
  475.         }
  476.         $asset $this->getRepo(Asset::class)->find($id);
  477.         if (!$asset) {
  478.             throw $this->createNotFoundException('The asset with ID "' $id '" was not found.');
  479.         }
  480.         $response         = new JsonResponse();
  481.         $assetMaintenance = new AssetMaintenance();
  482.         $form $this->createForm(AssetMaintenanceType::class);
  483.         $form->handleRequest($request);
  484.         if ($form->isSubmitted() && $form->isValid()) {
  485.             $assetMaintenance $form->getData();
  486.             $this->getAssetManager()->ensureCurrentLifecyclePeriod($asset);
  487.             $assetMaintenance->setAssetLifecyclePeriod($asset->getCurrentLifecyclePeriod());
  488.             $transaction = new AssetTransaction();
  489.             $transaction->setAssetLifecyclePeriod($asset->getCurrentLifecyclePeriod());
  490.             $transaction->setMaintenance($assetMaintenance);
  491.             $transaction->setAmount($assetMaintenance->getAmount());
  492.             $transaction->setTransactionCategory(TransactionCategory::OUT_TRUCK_MAINTENANCE_EXPENDITURE);
  493.             $assetMaintenance->setTransaction($transaction);
  494.             $em->persist($assetMaintenance);
  495.             $em->persist($transaction);
  496.             $em->flush();
  497.             $response->setData(['success'     => true'displayForm' => false'refresh'     => true]);
  498.             $this->setFlash('success''The maintenance "' $assetMaintenance->getDetail() . '" has been successfully created for asset "' $asset->getNameString() . '".');
  499.             return $response;
  500.         }
  501.         $response->setData(['success'     => false'displayForm' => true'refresh'     => false'isEdit'      => false'html'        => $this->renderView('Asset/_newMaintenanceForm.html.twig', ['form'      => $form->createView(), 'formRoute' => $this->generateUrl('new_asset_maintenance', ['id' => $id])])]);
  502.         return $response;
  503.     }
  504.     public function editMaintenance(Request $requestEntityManagerInterface $em$id null): JsonResponse
  505.     {
  506.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  507.         if ($id === null) {
  508.             throw $this->createNotFoundException('No truck model supplied.');
  509.         }
  510.         $assetMaintenance $this->getRepo(AssetMaintenance::class)->find($id);
  511.         $response         = new JsonResponse();
  512.         $form             $this->createForm(AssetMaintenanceType::class, $assetMaintenance);
  513.         $form->handleRequest($request);
  514.         if ($form->isSubmitted() && $form->isValid()) {
  515.             $assetMaintenance $form->getData();
  516.             $em->persist($assetMaintenance);
  517.             $transaction $assetMaintenance->getTransaction();
  518.             $transaction->setAmount($assetMaintenance->getAmount());
  519.             $em->persist($transaction);
  520.             $em->flush();
  521.             $response->setData(['success'     => true'displayForm' => false'message'     => 'The truck maintenance "' $assetMaintenance->getDetail() . '" was successfully updated.''refresh'     => true]);
  522.             $this->setFlash('success''The truck maintenance "' $assetMaintenance->getDetail() . '" was successfully updated.');
  523.             return $response;
  524.         }
  525.         $response->setData(['success'     => true'displayForm' => true'message'     => null'isEdit'      => true'refresh'     => false'html'        => $this->renderView('Asset/_newMaintenanceForm.html.twig', ['form'        => $form->createView(), 'formRoute'   => $this->generateUrl('edit_maintenance', ['id' => $id]), 'maintenance' => $assetMaintenance'isEdit'      => true])]);
  526.         return $response;
  527.     }
  528.     public function viewMaintenance(?string $id): Response
  529.     {
  530.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  531.         if (!$id) {
  532.             throw $this->createNotFoundException('No truck ID supplied.');
  533.         }
  534.         $maintenance $this->getRepo(AssetMaintenance::class)->find($id);
  535.         if (!$maintenance) {
  536.             throw $this->createNotFoundException('Truck with ID ' $id ' not found.');
  537.         }
  538.         $versions $this->getVersionRepo()->getLogEntries($maintenance);
  539.         return $this->render('Asset/viewMaintenance.html.twig', ['maintenance' => $maintenance'versions'    => $versions]);
  540.     }
  541.     public function deleteMaintenance(EntityManagerInterface $em$id null): RedirectResponse
  542.     {
  543.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  544.         $maintenance $this->getRepo(AssetMaintenance::class)->find($id);
  545.         if (!$maintenance) {
  546.             throw $this->createNotFoundException('The truck maintenance with ID"' $id '" was not found.');
  547.         }
  548.         $maintenanceDetail $maintenance->getDetail();
  549.         $truckId           $maintenance->getTruck()->getId();
  550.         $em->remove($maintenance);
  551.         $em->flush();
  552.         $this->setFlash('success''Maintenance "' $maintenanceDetail '" was successfully deleted');
  553.         return $this->redirect($this->generateUrl('view_truck', ['id' => $truckId]));
  554.     }
  555.     public function getTruckList(): JsonResponse
  556.     {
  557.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  558.         $trucks   $this->getRepo(Truck::class)->getActiveListings();
  559.         $response = new JsonResponse();
  560.         return $response->setData($trucks);
  561.     }
  562.     public function newTruckAttachment(Request $requestEntityManagerInterface $em): Response
  563.     {
  564.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  565.         $response   = new JsonResponse();
  566.         $attachment = new TruckAttachment();
  567.         $showCustomer $request->query->get('showCustomer') ?? false;
  568.         $form       $this->createForm(TruckAttachmentType::class, $attachment, ['showCustomer' => (bool) $showCustomer]);
  569.         $form->handleRequest($request);
  570.         if ($form->isSubmitted() && $form->isValid()) {
  571.             $noteDetail $form['detail']->getData();
  572.             if ($noteDetail) {
  573.                 $note = new Note();
  574.                 $hr   date('H');
  575.                 $min  date('i');
  576.                 $sec  date('s');
  577.                 $date $form['date']->getData();
  578.                 $date->setTime($hr$min$sec);
  579.                 $note->setDate($date);
  580.                 $note->setAsset($attachment);
  581.                 $note->setDetail($form['detail']->getData());
  582.                 $em->persist($note);
  583.             }
  584.             if ($showCustomer) {
  585.                 $customer $form['customer']->getData();
  586.                 $clientFleet $form['clientFleet']->getData();
  587.             } else {
  588.                 $customer $this->getRepo(Customer::class)->getDefaultCustomer();
  589.                 $clientFleet null;
  590.             }
  591.             if ($customer) {
  592.                 $period = new AssetLifecyclePeriod($attachment);
  593.                 $period->setCustomer($customer);
  594.                 $period->setClientFleet($clientFleet);
  595.                 $period->setStart(new \DateTime());
  596.                 $period->setType(AssetLifecyclePeriod::PERIOD_TYPE_CUSTOMER_OWNED);
  597.                 $em->persist($period);
  598.                 $em->flush();
  599.                 $attachment->setCurrentLifecyclePeriod($period);
  600.             }
  601.             $this->getAssetManager()->saveAsset($attachment);
  602.             $this->setFlash('success''Attachment created successfully.');
  603.             if ($request->query->get('assign'false)) {
  604.                 return $this->redirect($this->generateUrl('transfer_asset', ['id' => $attachment->getId()]));
  605.             }
  606.             $response->setData(['success'     => true'displayForm' => false'refresh'     => true]);
  607.             return $response;
  608.         }
  609.         $response->setData(['success'     => true'displayForm' => true'refresh'     => false'showCustomer' => $showCustomer'html'        => $this->renderView('TruckAttachment/_form.html.twig', ['form'       => $form->createView(), 'formRoute'  => $this->generateUrl('new_truck_attachment'), 'attachment' => $attachment])]);
  610.         return $response;
  611.     }
  612.     public function editTruckAttachment(Request $request$idEntityManagerInterface $em): JsonResponse
  613.     {
  614.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  615.         $response = new JsonResponse();
  616.         $attachment $this->getRepo(TruckAttachment::class)->find($id);
  617.         if (!$attachment) {
  618.             $response->setData(['success'     => false'displayForm' => false'refresh'     => false'message'     => 'The truck attachment was not found.']);
  619.             return $response;
  620.         }
  621.         $form $this->createForm(TruckAttachmentType::class, $attachment);
  622.         $form->handleRequest($request);
  623.         if ($form->isSubmitted() && $form->isValid()) {
  624.             $this->getAssetManager()->saveAsset($attachment);
  625.             $this->setFlash('success''Attachment updated successfully.');
  626.             $response->setData(['success'     => true'displayForm' => false'refresh'     => true]);
  627.             return $response;
  628.         }
  629.         $response->setData(['success'     => true'displayForm' => true'refresh'     => false'html'        => $this->renderView('TruckAttachment/_form.html.twig', ['form'       => $form->createView(), 'formRoute'  => $this->generateUrl('edit_truck_attachment', ['id' => $id]), 'attachment' => $attachment'isEdit'    => true])]);
  630.         return $response;
  631.     }
  632.     public function newTruckCharger(Request $requestEntityManagerInterface $em): Response
  633.     {
  634.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  635.         $response = new JsonResponse();
  636.         $charger  = new TruckCharger();
  637.         $showCustomer $request->query->get('showCustomer') ?? false;
  638.         $form     $this->createForm(TruckChargerType::class, $charger, ['showCustomer' => (bool) $showCustomer]);
  639.         $form->handleRequest($request);
  640.         if ($form->isSubmitted() && $form->isValid()) {
  641.             $noteDetail $form['detail']->getData();
  642.             if ($noteDetail) {
  643.                 $note = new Note();
  644.                 $hr   date('H');
  645.                 $min  date('i');
  646.                 $sec  date('s');
  647.                 $date $form['date']->getData();
  648.                 $date->setTime($hr$min$sec);
  649.                 $note->setDate($date);
  650.                 $note->setAsset($charger);
  651.                 $note->setDetail($form['detail']->getData());
  652.                 $em->persist($note);
  653.             }
  654.             if ($showCustomer) {
  655.                 $customer $form['customer']->getData();
  656.                 $clientFleet $form['clientFleet']->getData();
  657.             } else {
  658.                 $customer $this->getRepo(Customer::class)->getDefaultCustomer();
  659.                 $clientFleet null;
  660.             }
  661.             if ($customer) {
  662.                 $period = new AssetLifecyclePeriod($charger);
  663.                 $period->setCustomer($customer);
  664.                 $period->setClientFleet($clientFleet);
  665.                 $period->setStart(new \DateTime());
  666.                 $period->setType(AssetLifecyclePeriod::PERIOD_TYPE_CUSTOMER_OWNED);
  667.                 $em->persist($period);
  668.                 $em->flush();
  669.                 $charger->setCurrentLifecyclePeriod($period);
  670.             }
  671.             $this->getAssetManager()->saveAsset($charger);
  672.             $this->setFlash('success''Charger created successfully.');
  673.             if ($request->query->get('assign'false)) {
  674.                 return $this->redirect($this->generateUrl('transfer_asset', ['id' => $charger->getId()]));
  675.             }
  676.             $response->setData(['success'     => true'displayForm' => false'refresh'     => true]);
  677.             return $response;
  678.         }
  679.         $response->setData(['success'     => true'displayForm' => true'refresh'     => false'showCustomer' => $showCustomer'html'        => $this->renderView('TruckCharger/_form.html.twig', ['form'      => $form->createView(), 'formRoute' => $this->generateUrl('new_truck_charger'), 'charger'   => $charger])]);
  680.         return $response;
  681.     }
  682.     public function editTruckCharger(Request $request$idEntityManagerInterface $em): JsonResponse
  683.     {
  684.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  685.         $response = new JsonResponse();
  686.         $charger $this->getRepo(TruckCharger::class)->find($id);
  687.         if (!$charger) {
  688.             $response->setData(['success'     => false'displayForm' => false'refresh'     => false'message'     => 'The truck charger was not found.']);
  689.             return $response;
  690.         }
  691.         $form $this->createForm(TruckChargerType::class, $charger);
  692.         $form->handleRequest($request);
  693.         if ($form->isSubmitted() && $form->isValid()) {
  694.             $this->getAssetManager()->saveAsset($charger);
  695.             $this->setFlash('success''Charger updated successfully.');
  696.             $response->setData(['success'     => true'displayForm' => false'refresh'     => true]);
  697.             return $response;
  698.         }
  699.         $response->setData(['success'     => true'displayForm' => true'refresh'     => false'html'        => $this->renderView('TruckCharger/_form.html.twig', ['form'      => $form->createView(), 'formRoute' => $this->generateUrl('edit_truck_charger', ['id' => $id]), 'charger'   => $charger'isEdit'    => true])]);
  700.         return $response;
  701.     }
  702.     public function returnAsset(Request $request$idEntityManagerInterface $em): JsonResponse
  703.     {
  704.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  705.         $response = new JsonResponse();
  706.         $asset $this->getRepo(Asset::class)->find($id);
  707.         if (!$asset || !$asset->getCurrentLifecyclePeriod() || !$asset->getCurrentLifecyclePeriod()->getCustomer()) {
  708.             $error $asset 'The asset is not assigned.' 'The asset was not found.';
  709.             $response->setData(['success'     => false'displayForm' => false'refresh'     => false'message'     =>  $error]);
  710.             return $response;
  711.         }
  712.         $period $asset->getCurrentLifecyclePeriod();
  713.         $parentPeriod $period->getParentPeriod();
  714.         $form $this->createForm(EndAssetLifecyclePeriodType::class, $period);
  715.         $form->handleRequest($request);
  716.         if ($form->isSubmitted() && $form->isValid()) {
  717.             $endDate date_timestamp_get($period->getEnd());
  718.             if ($endDate time()) {
  719.                 $period->setScheduledLifecyclePeriodCleanup(true);
  720.             } else {
  721.                 if ($parentPeriod) {
  722.                     $asset->setCurrentLifecyclePeriod($parentPeriod);
  723.                 } else {
  724.                     $asset->setCurrentLifecyclePeriod(null);
  725.                     $this->getAssetManager()->ensureCurrentLifecyclePeriod($asset);
  726.                 }
  727.             }
  728.             if ($period->isContractHire() && $period->getResidualValue()) {
  729.                 $asset->setStockValue($period->getResidualValue());
  730.             } elseif ($period->isPurchase() && $period->getTradeInValue()) {
  731.                 $asset->setStockValue($period->getTradeInValue());
  732.             }
  733.             if (method_exists($asset'getAttachment')) {
  734.                 if ($attachment $asset->getAttachment()) {
  735.                     $attachmentPeriod $attachment->getCurrentLifecyclePeriod();
  736.                     $parentPeriod $attachmentPeriod->getParentPeriod();
  737.                     $attachmentPeriod->setEnd($period->getEnd());
  738.                     if ($endDate time()) {
  739.                         $attachmentPeriod->setScheduledLifecyclePeriodCleanup(true);
  740.                     } else {
  741.                         if ($parentPeriod) {
  742.                             $attachment->setCurrentLifecyclePeriod($parentPeriod);
  743.                         } else {
  744.                             $attachment->setCurrentLifecyclePeriod(null);
  745.                             $this->getAssetManager()->ensureCurrentLifecyclePeriod($attachment);
  746.                         }
  747.                     }
  748.                 }
  749.                 if ($charger $asset->getCharger()) {
  750.                     $chargerPeriod $charger->getCurrentLifecyclePeriod();
  751.                     $parentPeriod $chargerPeriod->getParentPeriod();
  752.                     $chargerPeriod->setEnd($period->getEnd());
  753.                     if ($endDate time()) {
  754.                         $chargerPeriod->setScheduledLifecyclePeriodCleanup(true);
  755.                     } else {
  756.                         if ($parentPeriod) {
  757.                             $charger->setCurrentLifecyclePeriod($parentPeriod);
  758.                         } else {
  759.                             $charger->setCurrentLifecyclePeriod(null);
  760.                             $this->getAssetManager()->ensureCurrentLifecyclePeriod($charger);
  761.                         }
  762.                     }
  763.                 }
  764.             }
  765.             $em->flush();
  766.             $this->setFlash('success''Asset returned successfully.');
  767.             $response->setData(['success'     => true'displayForm' => false'refresh'     => false'url'         => $this->generateUrl('view_asset', ['id' => $id])]);
  768.             return $response;
  769.         }
  770.         $response->setData(['success'     => true'displayForm' => true'refresh'     => false'html'        => $this->renderView('Asset/_returnForm.html.twig', ['form'      => $form->createView(), 'formRoute' => $this->generateUrl('return_asset', ['id' => $id])])]);
  771.         return $response;
  772.     }
  773.     public function transferAsset(Request $request$idEntityManagerInterface $em$processForm true): JsonResponse
  774.     {
  775.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  776.         $response = new JsonResponse();
  777.         $asset $this->getRepo(Asset::class)->find($id);
  778.         if (!$asset) {
  779.             $response->setData(['success'     => false'displayForm' => false'refresh'     => false'message'     => 'The asset was not found.']);
  780.             return $response;
  781.         }
  782.         $period = new AssetLifecyclePeriod($asset);
  783.         if ($asset->getCurrentLifecyclePeriod() && $asset->getCurrentLifecyclePeriod()->getCustomer() && !$asset->getCurrentLifecyclePeriod()->getCustomer()->getIsDefault()) {
  784.             $period->setType(AssetLifecyclePeriod::PERIOD_TYPE_CASUAL);
  785.         }
  786.         $form $this->createForm(AssetLifecyclePeriodType::class, $period);
  787.         $form->handleRequest($request);
  788.         if ($processForm && $form->isSubmitted() && $form->isValid()) {
  789.             $period->cleanup();
  790.             $currentPeriod $asset->getCurrentLifecyclePeriod();
  791.             // The form's setContacts() method is automatically called due to by_reference => false
  792.             // and it properly synchronizes both sides of the ManyToMany relationship
  793.             // We just need to persist the Contact entities (owning side) to ensure Doctrine detects changes
  794.             foreach ($period->getContacts() as $contact) {
  795.                 $em->persist($contact);
  796.             }
  797.             if ($currentPeriod) {
  798.                 if ($currentPeriod->getType() !== && $period->getType() === 2) {
  799.                     $period->setParentPeriod($currentPeriod);
  800.                 } else {
  801.                     if (!$currentPeriod->getEnd()) {
  802.                         $currentPeriod->setEnd(new \DateTime());
  803.                     }
  804.                 }
  805.                 $asset->setCurrentLifecyclePeriod(null);
  806.             }
  807.             $em->persist($period);
  808.             $asset->setCurrentLifecyclePeriod($period);
  809.             if ($asset instanceof Truck) {
  810.                 $attachment $asset->getAttachment() ? $asset->getAttachment()->getId() : null;
  811.                 $charger $asset->getCharger() ? $asset->getCharger()->getId() : null;
  812.                 if ($attachment) {
  813.                     $attachment $this->getRepo(TruckAttachment::class)->find($attachment);
  814.                     $attachmentCurrentPeriod $attachment->getCurrentLifecyclePeriod();
  815.                     $attachmentPeriod = new AssetLifecyclePeriod();
  816.                     $attachmentPeriod->setAsset($attachment);
  817.                     $attachmentPeriod->setStart($period->getStart());
  818.                     $attachmentPeriod->setEnd($period->getEnd());
  819.                     $attachmentPeriod->setUnderwriter($period->getUnderwriter());
  820.                     $attachmentPeriod->setCustomer($period->getCustomer());
  821.                     $attachmentPeriod->setType($period->getType());
  822.                     if ($attachmentCurrentPeriod) {
  823.                         if ($attachmentCurrentPeriod->getType() !== && $attachmentPeriod->getType() === 2) {
  824.                             $attachmentPeriod->setParentPeriod($attachmentCurrentPeriod);
  825.                         } else {
  826.                             if (!$attachmentCurrentPeriod->getEnd()) {
  827.                                 $attachmentCurrentPeriod->setEnd(new \DateTime());
  828.                             }
  829.                         }
  830.                         $attachment->setCurrentLifecyclePeriod(null);
  831.                     }
  832.                     $em->persist($attachmentPeriod);
  833.                     $attachment->setCurrentLifecyclePeriod($attachmentPeriod);
  834.                 }
  835.                 if ($charger) {
  836.                     $charger $this->getRepo(TruckCharger::class)->find($charger);
  837.                     $chargerCurrentPeriod $charger->getCurrentLifecyclePeriod();
  838.                     $chargerPeriod = new AssetLifecyclePeriod();
  839.                     $chargerPeriod->setAsset($charger);
  840.                     $chargerPeriod->setStart($period->getStart());
  841.                     $chargerPeriod->setEnd($period->getEnd());
  842.                     $chargerPeriod->setUnderwriter($period->getUnderwriter());
  843.                     $chargerPeriod->setCustomer($period->getCustomer());
  844.                     $chargerPeriod->setType($period->getType());
  845.                     if ($chargerCurrentPeriod) {
  846.                         if ($chargerCurrentPeriod->getType() !== && $chargerPeriod->getType() === 2) {
  847.                             $chargerPeriod->setParentPeriod($chargerCurrentPeriod);
  848.                         } else {
  849.                             if (!$chargerCurrentPeriod->getEnd()) {
  850.                                 $chargerCurrentPeriod->setEnd(new \DateTime());
  851.                             }
  852.                         }
  853.                         $charger->setCurrentLifecyclePeriod(null);
  854.                     }
  855.                     $em->persist($chargerPeriod);
  856.                     $charger->setCurrentLifecyclePeriod($chargerPeriod);
  857.                 }
  858.             }
  859.             $em->flush();
  860.             $this->setFlash('success''Asset transferred successfully.');
  861.             $response->setData(['success'     => true'displayForm' => false'refresh'     => false'url'         => $this->generateUrl('view_asset', ['id' => $id])]);
  862.             return $response;
  863.         }
  864.         $response->setData(['success'     => true'displayForm' => true'refresh'     => false'html'        => $this->renderView('Asset/_transferForm.html.twig', ['form'            => $form->createView(), 'formRoute'       => $this->generateUrl('transfer_asset', ['id' => $id]), 'reloadFormRoute' => $this->generateUrl('transfer_asset_form', ['id' => $id])])]);
  865.         return $response;
  866.     }
  867.     public function editLifecyclePeriod(Request $request$idEntityManagerInterface $em$processForm true): JsonResponse
  868.     {
  869.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  870.         $response = new JsonResponse();
  871.         $period $this->getRepo(AssetLifecyclePeriod::class)->find($id);
  872.         $asset $this->getRepo(Asset::class)->find($period->getAsset()->getId());
  873.         $isTruck $asset->getAssetType() === 'Truck';
  874.         $attachment $isTruck && $asset->getAttachment() ? $asset->getAttachment()->getCurrentLifecyclePeriod() : null;
  875.         $charger $isTruck && $asset->getCharger() ? $asset->getCharger()->getCurrentLifecyclePeriod() : null;
  876.         if (!$period) {
  877.             $response->setData(['success'     => false'displayForm' => false'refresh'     => false'message'     => 'The asset status was not found.']);
  878.             return $response;
  879.         }
  880.         // Store original contacts before form submission to track removals
  881.         $originalContacts = new ArrayCollection();
  882.         foreach ($period->getContacts() as $contact) {
  883.             $originalContacts->add($contact);
  884.         }
  885.         $form $this->createForm(AssetLifecyclePeriodType::class, $period);
  886.         $form->handleRequest($request);
  887.         if ($processForm && $form->isSubmitted() && $form->isValid()) {
  888.             $period->cleanup();
  889.             // The form's setContacts() method is automatically called due to by_reference => false
  890.             // and it properly synchronizes both sides of the ManyToMany relationship
  891.             // Persist Contact entities (owning side) to ensure Doctrine detects the changes
  892.             foreach ($period->getContacts() as $contact) {
  893.                 $em->persist($contact);
  894.             }
  895.             // Also persist contacts that were removed to ensure join table cleanup
  896.             foreach ($originalContacts as $contact) {
  897.                 if (!$period->getContacts()->contains($contact)) {
  898.                     $em->persist($contact);
  899.                 }
  900.             }
  901.             $em->persist($period);
  902.             if ($attachment) {
  903.                 $periodAttachment $this->getRepo(AssetLifecyclePeriod::class)->find($attachment);
  904.                 $periodAttachment->setStart($form['start']->getData());
  905.                 $periodAttachment->setEnd($form['end']->getData());
  906.                 $periodAttachment->setEnd($form['end']->getData());
  907.                 $periodAttachment->setUnderwriter($form['underwriter']->getData());
  908.                 $periodAttachment->setCustomer($form['customer']->getData());
  909.                 $periodAttachment->setType($form['type']->getData());
  910.                 $em->persist($periodAttachment);
  911.             }
  912.             if ($charger) {
  913.                 $periodCharger $this->getRepo(AssetLifecyclePeriod::class)->find($charger);
  914.                 $periodCharger->setStart($form['start']->getData());
  915.                 $periodCharger->setEnd($form['end']->getData());
  916.                 $periodCharger->setUnderwriter(isset($form['underwriter']) ? $form['underwriter']->getData() : '');
  917.                 $periodCharger->setCustomer($form['customer']->getData());
  918.                 $periodCharger->setType($form['type']->getData());
  919.                 $em->persist($periodCharger);
  920.             }
  921.             $em->flush();
  922.             // Clear the entity manager to ensure subsequent queries fetch fresh data
  923.             // This is critical for email notifications to use the updated contact list
  924.             $em->clear(AssetLifecyclePeriod::class);
  925.             $em->clear(Contact::class);
  926.             $this->setFlash('success''Asset status updated successfully.');
  927.             $response->setData(['success'     => true'displayForm' => false'refresh'     => false'url'         => $this->generateUrl('view_asset', ['id' => $period->getAsset()->getId()])]);
  928.             return $response;
  929.         }
  930.         $response->setData(['success'     => true'displayForm' => true'refresh'     => false'html'        => $this->renderView('Asset/_transferForm.html.twig', ['form'            => $form->createView(), 'formRoute'       => $this->generateUrl('edit_asset_lifecycle_period', ['id' => $id]), 'reloadFormRoute' => $this->generateUrl('edit_asset_lifecycle_period_form', ['id' => $id])])]);
  931.         return $response;
  932.     }
  933.     public function autocomplete(Request $request): JsonResponse
  934.     {
  935.         $searchData $request->query->all();
  936.         $response = new JsonResponse([]);
  937.         if (!array_key_exists('type'$searchData) || !array_key_exists('search'$searchData) || !array_key_exists('customer'$searchData)) {
  938.             return $response;
  939.         }
  940.         $type null;
  941.         switch ($searchData['type']) {
  942.             case 'Truck':
  943.                 $type Truck::class;
  944.                 break;
  945.             case 'TruckAttachment':
  946.                 $type TruckAttachment::class;
  947.                 break;
  948.             case 'TruckCharger':
  949.                 $type TruckCharger::class;
  950.                 break;
  951.         }
  952.         $assets $this->getRepo($type)->findItemsForAutocomplete($searchData['search'], $searchData['customer']);
  953.         $responseData array_map(fn ($asset): array => ['id' => $asset->getCurrentLifecyclePeriod()->getId(), 'name' => $asset->getCurrentLifecyclePeriod()->getClientFleet() . ' - ' $asset->getNameString()], $assets);
  954.         $response->setData($responseData);
  955.         return $response;
  956.     }
  957.     /**
  958.      * @Route("/assets/archive/{id}", name="archive_asset", methods={"POST"})
  959.      */
  960.     public function archiveAsset(EntityManagerInterface $em$id null): JsonResponse
  961.     {
  962.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  963.         $response = new JsonResponse();
  964.         $asset $this->getRepo(Asset::class)->find($id);
  965.         if (!$asset) {
  966.             $response->setData(['success' => false'message' => 'Asset not found.']);
  967.             return $response;
  968.         }
  969.         $asset->setArchived(true);
  970.         $asset->setArchivedAt(new \DateTime());
  971.         $em->persist($asset);
  972.         $em->flush();
  973.         $this->setFlash('success''Asset "' $asset->getNameString() . '" has been successfully archived.');
  974.         $response->setData(['success' => true'message' => 'Asset archived successfully.']);
  975.         return $response;
  976.     }
  977.     /**
  978.      * @Route("/assets/unarchive/{id}", name="unarchive_asset", methods={"POST"})
  979.      */
  980.     public function unarchiveAsset(EntityManagerInterface $em$id null): JsonResponse
  981.     {
  982.         $this->securityCheck('ROLE_SERVICE_ADMIN');
  983.         $response = new JsonResponse();
  984.         $asset $this->getRepo(Asset::class)->find($id);
  985.         if (!$asset) {
  986.             $response->setData(['success' => false'message' => 'Asset not found.']);
  987.             return $response;
  988.         }
  989.         $asset->setArchived(false);
  990.         $asset->setArchivedAt(null);
  991.         $em->persist($asset);
  992.         $em->flush();
  993.         $this->setFlash('success''Asset "' $asset->getNameString() . '" has been successfully unarchived.');
  994.         $response->setData(['success' => true'message' => 'Asset unarchived successfully.']);
  995.         return $response;
  996.     }
  997. }