<?php
namespace App\V4\Form\Type\Quote;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use App\Form\Type\AbstractViewOrderAwareType;
use App\Form\Type\Compare\DateCompareType;
use App\Form\Type\Compare\NumberCompareType;
use App\Form\Type\SubresourceChoicesTrait;
use App\Listing\Transformer\ListingResponseTransformer;
use App\Model\IriNormalizableInterface;
use App\Model\Traits\TimeStampableFormType;
use App\Model\ViewOrder\ViewOrder;
use App\Security\SecurityConfig;
use App\Service\ApiWebServiceFilterBuilder\ListWithEmptyFilterBuilder;
use App\Service\Cache\CacheManager;
use App\V4\Form\AsyncSubresourceChoicesLoader;
use App\V4\Form\Type\IdSearchTypeTrait;
use App\V4\Form\Type\ManagedByFilterTrait;
use App\V4\Form\Type\SectionNameFilterTrait;
use App\V4\Logger\SentryLogger;
use App\V4\Model\Prospect\Prospect;
use App\V4\Model\Quote\Quote;
use App\V4\Model\Quote\QuoteSearch;
use App\V4\Model\QuoteReason\QuoteReason;
use App\V4\Model\QuoteState\QuoteState;
use Psr\Cache\CacheException;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
class QuoteSearchType extends AbstractViewOrderAwareType
{
use ManagedByFilterTrait;
use SectionNameFilterTrait;
use TimeStampableFormType;
use SubresourceChoicesTrait {
getEntityChoices as private getEntityChoicesTrait;
}
use IdSearchTypeTrait;
/**
* @var CollectionDataProviderInterface
*/
private $collectionDataProvider;
/**
* @var CacheManager
*/
private $cacheManager;
/**
* @var Security
*/
private $security;
/**
* @var SentryLogger
*/
private $sentryLogger;
/**
* @param CollectionDataProviderInterface $collectionDataProvider
* @param CacheManager $cacheManager
* @param Security $security
* @param SentryLogger $sentryLogger
*/
public function __construct(
CollectionDataProviderInterface $collectionDataProvider,
CacheManager $cacheManager,
Security $security,
SentryLogger $sentryLogger
) {
$this->collectionDataProvider = $collectionDataProvider;
$this->cacheManager = $cacheManager;
$this->security = $security;
$this->sentryLogger = $sentryLogger;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*
* @return void
*
* @throws CacheException
* @throws InvalidArgumentException
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
parent::buildForm($builder, $options);
$this->addIdsField($builder);
$builder
->add('name', TextType::class, [
'label' => 'quote_title',
])
->add('quoteNumber', TextType::class)
->add('contractNumber', TextType::class)
->add('isMaintenance', CheckboxType::class, [
'false_values' => [null, '0', 0, false, '', 'false'],
])
->add('myQuotes', CheckboxType::class, [
'false_values' => [null, '0', 0, false, '', 'false'],
'data' => $this->security->isGranted(SecurityConfig::MY_QUOTE_BY_DEFAULT),
])
->add('status', ChoiceType::class, [
'choices' => $this->getEntityChoices(QuoteState::class, [], 'name'),
'choice_label' => 'name',
'choice_value' => 'id',
'multiple' => true,
])
->add('salesForecast', ChoiceType::class, [
'choices' => [
'yes' => 'yes',
'no' => 'no',
'all' => 'all',
],
'expanded' => true,
])
->add('issuedAt', DateCompareType::class, [
'attr' => [
'type' => 'date_compare',
],
])
->add('expectedSignedAt', DateCompareType::class, [
'attr' => [
'type' => 'date_compare',
],
])
->add('warrantlyAt', DateCompareType::class, [
'attr' => [
'type' => 'date_compare',
],
])
->add('expiredAt', DateCompareType::class, [
'attr' => [
'type' => 'date_compare',
],
])
->add('prospects', ChoiceType::class, [
'label' => 'prospects_clients',
'multiple' => true,
'choice_label' => 'fullname',
'choice_value' => 'id',
'choice_loader' => new AsyncSubresourceChoicesLoader(
$this->collectionDataProvider,
$this->cacheManager,
$this->sentryLogger,
Prospect::class,
'id'
),
'attr' => [
'type' => 'autocomplete',
'autocomplete_entity' => 'prospect',
ListingResponseTransformer::FORM_FILTER_KEY => 'prospectId',
],
])
->add('managedBy', ChoiceType::class, [
'choices' => $this->getManagedByChoices(),
'multiple' => true,
])
->add('totalExcludingVat', NumberCompareType::class)
->add('potential', IntegerType::class, [
'label' => 'quote_potential',
'attr' => ['min' => 0, 'max' => 100, 'step' => 25, 'type' => 'slider'],
])
->add('sectionName', ChoiceType::class, [
'choices' => $this->getSectionNameChoices(),
])
->add('prospectIds', ChoiceType::class, [
'label' => 'prospectIds',
'required' => false,
'attr' => [
'type' => 'hidden',
ListingResponseTransformer::FORM_FILTER_KEY => 'prospectId',
],
'multiple' => 'true',
])
->add('productIds', ChoiceType::class, [
'label' => 'productIds',
'required' => false,
'attr' => ['type' => 'hidden'],
'multiple' => 'true',
])
->add('contactIds', ChoiceType::class, [
'label' => 'contactIds',
'required' => false,
'attr' => [
'type' => 'hidden',
ListingResponseTransformer::FORM_FILTER_KEY => 'contactId',
],
'multiple' => 'true',
])
->add('reason', ChoiceType::class, [
'choices' => $this->getEntityChoices(QuoteReason::class, [], 'name'),
'choice_label' => 'name',
'choice_value' => 'id',
'multiple' => true,
])
->add('reasonComment', TextareaType::class, [
'required' => false,
])
->add('fileLabel', TextType::class, [
'label' => 'quote_search_file_label',
])
;
$builder->get('prospectIds')->resetViewTransformers();
$builder->get('productIds')->resetViewTransformers();
$builder->get('contactIds')->resetViewTransformers();
//La recherche enchainée des prospects est prioritaire sur le champ de recherche prospect classique
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
if (!empty($data['prospectIds'])) {
$data['prospects'] = $data['prospectIds'];
$data['prospectIds'] = [];
$event->setData($data);
}
});
$this->addTimeStampableFields($builder, '_quote');
}
/**
* @param OptionsResolver $resolver
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'csrf_protection' => false,
'data_class' => QuoteSearch::class,
'required' => false,
self::FORM_CONFIG_VIEW_ORDER_ENTITY => Quote::class,
self::FORM_CONFIG_VIEW_ORDER_TYPE => ViewOrder::VIEWORDER_TYPE_SEARCH,
self::FORM_CONFIG_SPECIFIC_FIELD_ENTITY => Quote::class,
self::FORM_CONFIG_VIEW_ORDER_ENTITY_TYPE => null,
]);
}
/**
* @param string $entityFQCN
* @param array $filters
* @param string $labelProperty
* @param string $valueProperty
*
* @return array<string, object>
*
* @throws CacheException
* @throws InvalidArgumentException
*/
private function getEntityChoices(string $entityFQCN, array $filters = [], string $labelProperty = 'value', string $valueProperty = 'id'): array
{
$choices = $this->getEntityChoicesTrait(
$this->collectionDataProvider,
$this->cacheManager,
$this->sentryLogger,
$entityFQCN,
$filters
);
$valueSetter = 'set'.ucfirst($valueProperty);
$labelSetter = 'set'.ucfirst($labelProperty);
$emptyValue = (new $entityFQCN())
->$valueSetter(ListWithEmptyFilterBuilder::EMPTY_VALUE)
->$labelSetter('(Vide)')
;
if ($emptyValue instanceof IriNormalizableInterface) {
$emptyValue->setNormalizeAsIRI(true);
}
$choices[] = $emptyValue;
return $choices;
}
}