src/Admin/ReserveActiveAdmin.php line 25

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Admin;
  4. use App\Entity\Reserve;
  5. use App\Entity\Transaction;
  6. use App\Entity\Type\DirectionType;
  7. use App\Entity\Type\RentStatusType;
  8. use App\Repository\ReserveRepository;
  9. use App\Service\TransactionService;
  10. use Doctrine\ORM\EntityManagerInterface;
  11. use Doctrine\ORM\QueryBuilder;
  12. use Sonata\AdminBundle\Admin\AbstractAdmin;
  13. use Sonata\AdminBundle\Datagrid\DatagridMapper;
  14. use Sonata\AdminBundle\Datagrid\ListMapper;
  15. use Sonata\AdminBundle\Form\FormMapper;
  16. use Sonata\AdminBundle\Form\Type\ModelType;
  17. use Sonata\AdminBundle\Route\RouteCollection;
  18. use Sonata\AdminBundle\Show\ShowMapper;
  19. use Sonata\Form\Validator\ErrorElement;
  20. use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
  21. final class ReserveActiveAdmin extends AbstractReserveAdmin
  22. {
  23.     private EntityManagerInterface $entityManager;
  24.     private ?TransactionService $transactionService null;
  25.     public function createQuery($context 'list')
  26.     {
  27.         /** @var QueryBuilder $query */
  28.         $query parent::createQuery();
  29.         $alias $query->getRootAliases()[0];
  30.         $query->andWhere($alias .'.status = :statue');
  31.         $query->setParameter('statue'RentStatusType::ACTIVE);
  32.         return $query;
  33.     }
  34.     protected $datagridValues = [
  35.         '_sort_order' => 'DESC',
  36.         '_sort_by' => 'and_date'
  37.     ];
  38.     protected function configureDatagridFilters(DatagridMapper $datagridMapper): void
  39.     {
  40.         $datagridMapper
  41.             ->add('id')
  42.             ->add('startDate')
  43.             ->add('endDate')
  44.             ;
  45.     }
  46.     protected function configureListFields(ListMapper $listMapper): void
  47.     {
  48.         $listMapper->add('motorcycle');
  49.         $listMapper->add('client');
  50.         $listMapper->add('startDate'null, ['template' => 'admin/Reserve/list_start_date.html.twig']);
  51.         $listMapper->add('endDate'null, ['template' => 'admin/Reserve/list_end_date.html.twig']);
  52.         $listMapper->add('amount'null, ['template' => 'admin/Reserve/list_amount.html.twig']);
  53.         $listMapper->add('acceptanceCertificate'null, [
  54.             'label' => 'Действия',
  55.             'template' => 'admin/Reserve/Active/list_action.html.twig'
  56.         ]);
  57.     }
  58.     protected function configureFormFields(FormMapper $formMapper): void
  59.     {
  60.         $formMapper->with('Данные резерва', ['class' => 'col-md-6 box-transition']);
  61.         $formMapper->add('motorcycle'ModelType::class, [
  62.             'btn_add' => false,
  63.             'multiple' => false,
  64.             'label' => 'Мотоцикл',
  65.             'error_bubbling' => true,
  66.             'attr' => ['data-reserve-field' => 'motorcycle'],
  67.         ]);
  68.         $formMapper->add('startDate'DateTimeType::class, [
  69.             'widget' => 'single_text',
  70.             'required' => false,
  71.             'error_bubbling' => true,
  72.             'label' => 'Дата и время начала аренды',
  73.             'attr' => ['data-reserve-field' => 'start-date'],
  74.         ]);
  75.         $formMapper->add('endDate'DateTimeType::class, [
  76.             'widget' => 'single_text',
  77.             'required' => false,
  78.             'error_bubbling' => true,
  79.             'label' => 'Дата и время окончания аренды',
  80.             'attr' => ['data-reserve-field' => 'end-date'],
  81.         ]);
  82.         $formMapper->add('amount'null, [
  83.             'label' => 'Стоимость аренды (BYN)',
  84.             'attr' => ['data-reserve-field' => 'amount'],
  85.         ]);
  86.         $formMapper->end();
  87.         $formMapper->with('Клиент', ['class' => 'col-md-6 box-transition']);
  88.         $formMapper->add('client'ModelType::class, [
  89.             'btn_add' => 'Новый клиент',
  90.             'multiple' => false,
  91.             'label' => 'Клиент',
  92.         ]);
  93.         $formMapper->add('notes'null, ['label' => 'Заметки']);
  94.         $formMapper->end();
  95.     }
  96.     protected function configureShowFields(ShowMapper $showMapper): void
  97.     {
  98.         $showMapper
  99.             ->add('id')
  100.             ->add('startDate')
  101.             ->add('endDate')
  102.             ;
  103.     }
  104.     /**
  105.      * {@inheritdoc}
  106.      * @param $object Reserve
  107.      */
  108.     public function validate(ErrorElement $errorElement$object) {
  109.         /** @var ReserveRepository $reserveRepository */
  110.         $reserveRepository $this->entityManager->getRepository(Reserve::class);
  111.         $intersections $reserveRepository->reserveList(clone $object->getStartDate(),(clone $object->getEndDate())->modify('- 1 second'), $object->getId(), $object->getMotorcycle());
  112.         if ($intersections->count() > 0) {
  113.             $intersections->map(static function (Reserve $reserve) use (&$errorElement) {
  114.                 $errorElement->with('motorcycle')->addViolation(sprintf('Пересечение: %s - %s (%s)',
  115.                     $reserve->getStartDate()->format('Y-m-d H:i:s'),
  116.                     $reserve->getEndDate()->format('Y-m-d H:i:s'),
  117.                     $reserve->getClient()
  118.                 ))->end();
  119.             });
  120.         }
  121.     }
  122.     /**
  123.      * Override to keep ACTIVE status when editing from active list.
  124.      * Abstract forces APPROVED which would be wrong here.
  125.      * Also sync the linked transaction amount when the reserve amount is updated.
  126.      *
  127.      * @param $object Reserve
  128.      */
  129.     public function preUpdate($object): void
  130.     {
  131.         if ($object->getEndDate()->format('s') === "00") {
  132.             $object->setEndDate($object->getEndDate()->modify('- 1 second'));
  133.         }
  134.         // Preserve ACTIVE (or whatever current) status. Do not force APPROVED.
  135.         $object->setAdmin($this->getCurrentUser());
  136.         // When amount (or other) is edited on active reserve, keep the transaction in sync.
  137.         $this->syncTransactionAmount($object);
  138.     }
  139.     /**
  140.      * When an active rental is deleted (motorcycle swap / cancel), reverse the income with an outgoing TX.
  141.      * Button "Отменить" uses admin.reserve delete; this covers delete from active admin context too.
  142.      *
  143.      * @param Reserve $object
  144.      */
  145.     public function preRemove($object): void
  146.     {
  147.         if (!$object instanceof Reserve) {
  148.             return;
  149.         }
  150.         if ((int) $object->getStatus() !== RentStatusType::ACTIVE) {
  151.             return;
  152.         }
  153.         if ($this->transactionService) {
  154.             $this->transactionService->createReserveOutgoingTransaction($object);
  155.         }
  156.     }
  157.     /**
  158.      * Find transaction(s) linked to this reserve via targetEntity/targetId and update their amount.
  159.      * This fulfills the requirement: changing reserve amount on edit must update the transaction.
  160.      *
  161.      * @param Reserve $reserve
  162.      */
  163.     private function syncTransactionAmount(Reserve $reserve): void
  164.     {
  165.         if (!$this->entityManager || $reserve->getAmount() === null) {
  166.             return;
  167.         }
  168.         $txRepo $this->entityManager->getRepository(Transaction::class);
  169.         // Only income (IN) transactions — never rewrite reverse OUT entries.
  170.         $transactions $txRepo->findBy([
  171.             'targetEntity' => Reserve::class,
  172.             'targetId' => $reserve->getId(),
  173.             'direction' => DirectionType::IN,
  174.         ]);
  175.         $description $this->transactionService
  176.             $this->transactionService->buildReserveIncomeDescription($reserve)
  177.             : ('Пополнение по аренде №' $reserve->getId());
  178.         foreach ($transactions as $transaction) {
  179.             $transaction->setAmount((float) $reserve->getAmount());
  180.             $transaction->setDescription($description);
  181.         }
  182.         // If for some reason there is no transaction yet for this active reserve, create one
  183.         // (defensive; normally created during the "to active / sign act" flow).
  184.         if (count($transactions) === && $reserve->getAmount() > 0) {
  185.             if ($this->transactionService) {
  186.                 $this->transactionService->createReserveIncomeTransaction($reserve);
  187.             } else {
  188.                 $tx = new Transaction();
  189.                 $tx->setAmount((float) $reserve->getAmount());
  190.                 $tx->setDirection(DirectionType::IN);
  191.                 $tx->setDescription($description);
  192.                 $tx->setTargetEntity(Reserve::class);
  193.                 $tx->setTargetId($reserve->getId());
  194.                 $this->entityManager->persist($tx);
  195.             }
  196.         }
  197.     }
  198.     /**
  199.      * @required
  200.      * @param EntityManagerInterface $entityManager
  201.      */
  202.     public function setEntityManager(EntityManagerInterface $entityManager): void
  203.     {
  204.         $this->entityManager $entityManager;
  205.     }
  206.     /**
  207.      * @required
  208.      */
  209.     public function setTransactionService(TransactionService $transactionService): void
  210.     {
  211.         $this->transactionService $transactionService;
  212.     }
  213.     protected function configureRoutes(RouteCollection $collection): void
  214.     {
  215.         parent::configureRoutes($collection);
  216.         $collection->add('get_reserve_calendar''get-reserve-calendar');
  217.         $collection->add('create_acceptance_certificate''create-acceptance-certificate');
  218.         $collection->add('view_acceptance_certificate''view-acceptance-certificate');
  219.         // Allow return/close flow directly from active admin context
  220.         $collection->add('close_reserve''close-reserve');
  221.         $collection->add('finale_close_reserve''finale-close-reserve');
  222.         $collection->add('add_to_reserve''add-to-reserve');
  223.         $collection->get('add_to_reserve')->setMethods('POST');
  224.         $collection->add('calculate_amount''calculate-amount');
  225.     }
  226.     public function setBaseRouteName(string $baseRouteName): void
  227.     {
  228.         $this->baseRouteName $baseRouteName;
  229.     }
  230.     public function setBaseRoutePattern(string $baseRoutePattern): void
  231.     {
  232.         $this->baseRoutePattern $baseRoutePattern;
  233.     }
  234.     public function setClassnameLabel(string $label): void
  235.     {
  236.         $this->classnameLabel $label;
  237.     }
  238. }