src/Entity/Crm/Opportunity/Opportunity.php line 77

Open in your IDE?
  1. <?php
  2. namespace App\Entity\Crm\Opportunity;
  3. use App\Entity\Account\User;
  4. use App\Entity\Channel\Channel;
  5. use App\Entity\Channel\ChannelContact;
  6. use App\Entity\Channel\Company;
  7. use App\Entity\Channel\Drive;
  8. use App\Entity\Channel\Place;
  9. use App\Entity\Channel\Training;
  10. use App\Entity\Common\Contact;
  11. use App\Entity\Crm\ActivityFeed\ActivityFeed;
  12. use App\Entity\Crm\Target\HasTargetInterface;
  13. use App\Entity\Crm\Target\HasTargetTrait;
  14. use App\Entity\Crm\Target\TargetableInterface;
  15. use App\Entity\Crm\Contact\ChannelContactRelationInterface;
  16. use App\Entity\Crm\Contact\HasChannelContactsInterface;
  17. use App\Entity\Crm\Contact\HasChannelContactsMethodTrait;
  18. use App\Entity\Crm\Embed\Money;
  19. use App\Entity\Crm\Embed\TextContent;
  20. use App\Entity\Crm\HasStatusInterface;
  21. use App\Entity\Crm\Pipeline\Pipeline;
  22. use App\Entity\Crm\Pipeline\PipelineStep;
  23. use App\Entity\Crm\Quote\Document\Quote;
  24. use App\Entity\Crm\Trait\HasStatusTrait;
  25. use App\Entity\Crm\Trait\HasTargetValidationTrait;
  26. use App\Entity\Crm\Trait\IgnoreUpdatedCurrentStepFieldTrait;
  27. use App\Entity\Crm\Trait\TextContentProxyTrait;
  28. use App\Enum\CurrencyEnum;
  29. use App\Enum\Status\OpportunityStatusEnum;
  30. use App\Enum\TargetTypeEnum;
  31. use App\Enum\Serialization\OpportunitySerializationGroups;
  32. use App\Enum\Validation\OpportunityValidationGroups;
  33. use App\Repository\Crm\Opportunity\OpportunityRepository;
  34. use DateTimeImmutable;
  35. use DateTimeInterface;
  36. use Doctrine\Common\Collections\ArrayCollection;
  37. use Doctrine\Common\Collections\Collection;
  38. use Doctrine\DBAL\Types\Types;
  39. use Doctrine\ORM\Mapping as ORM;
  40. use Doctrine\ORM\Mapping\JoinColumn;
  41. use DrosalysWeb\ObjectExtensions\Blame\Model\BlameInterface;
  42. use DrosalysWeb\ObjectExtensions\Blame\Model\BlameTrait;
  43. use DrosalysWeb\ObjectExtensions\Blame\Model\IgnoreUpdatedBlameInterface;
  44. use DrosalysWeb\ObjectExtensions\Timestamp\Model\IgnoreUpdatedTimestampInterface;
  45. use DrosalysWeb\ObjectExtensions\Timestamp\Model\TimestampInterface;
  46. use DrosalysWeb\ObjectExtensions\Timestamp\Model\TimestampTrait;
  47. use Nellapp\Bundle\SDKBundle\Archivable\Entity\ArchivableInterface;
  48. use Nellapp\Bundle\SDKBundle\Archivable\Entity\ArchivableTrait;
  49. use Nellapp\Bundle\SDKBundle\Channel\Entity\ChannelInterface;
  50. use Nellapp\Bundle\SDKBundle\Drive\Entity\DriveInterface;
  51. use Nellapp\Bundle\SDKBundle\Drive\Entity\HasDriveInterface;
  52. use Nellapp\Bundle\SDKBundle\Drive\Entity\HasDriveTrait;
  53. use Nellapp\Bundle\SDKBundle\Enum\ChannelUserData\CursusCourseEnum;
  54. use Nellapp\Bundle\SDKBundle\Enum\ChannelUserData\CursusModalityEnum;
  55. use Nellapp\Bundle\SDKBundle\Enum\ChannelUserData\RqthStatusEnum;
  56. use Nellapp\Bundle\SDKBundle\Permission\Entity\ChannelResourceInterface;
  57. use Nellapp\Bundle\SDKBundle\Planning\Entity\PlanningResourceInterface;
  58. use Nellapp\Bundle\SDKBundle\Serializer\SerialRelation\Attribute\SerialRelation;
  59. use Nellapp\Bundle\SDKBundle\TrackData\Attribute\TrackEntity;
  60. use Nellapp\Bundle\SDKBundle\TrackData\Attribute\TrackField;
  61. use Nellapp\Bundle\SDKBundle\Utils\NellappFormatService;
  62. use Symfony\Component\Serializer\Annotation as Serial;
  63. use Symfony\Component\Validator\Constraints as Assert;
  64. /**
  65.  * @see OpportunityNormalizer for some serialization logic
  66.  */
  67. #[ORM\Entity(repositoryClassOpportunityRepository::class),
  68.     ORM\Table(name'crm_opportunity'),
  69.     TrackEntity(
  70.         fieldGroups: [
  71.             'informations' => [],
  72.         ]
  73.     )]
  74. class Opportunity implements TimestampInterfaceIgnoreUpdatedTimestampInterfaceBlameInterfaceIgnoreUpdatedBlameInterfaceArchivableInterfaceChannelResourceInterfaceHasChannelContactsInterfaceHasStatusInterfaceHasTargetInterfaceHasDriveInterface
  75. {
  76.     use BlameTrait;
  77.     use TimestampTrait;
  78.     use ArchivableTrait;
  79.     use TextContentProxyTrait;
  80.     use HasChannelContactsMethodTrait;
  81.     use HasStatusTrait;
  82.     use HasTargetTrait;
  83.     use HasTargetValidationTrait;
  84.     use IgnoreUpdatedCurrentStepFieldTrait;
  85.     use HasDriveTrait;
  86.     #[ORM\Id]
  87.     #[ORM\GeneratedValue]
  88.     #[ORM\Column]
  89.     #[Serial\Groups([
  90.         OpportunitySerializationGroups::DETAIL,
  91.         OpportunitySerializationGroups::COLLECTION,
  92.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  93.         OpportunitySerializationGroups::API_COLLECTION,
  94.     ])]
  95.     private ?int $id null;
  96.     #[ORM\ManyToOne(targetEntityChannel::class)]
  97.     #[JoinColumn(nullablefalseonDelete'CASCADE')]
  98.     private ?Channel $channel null;
  99.     #[ORM\Embedded(class: TextContent::class, columnPrefixfalse)]
  100.     #[Assert\Valid()]
  101.     #[TrackField(name""fieldKey'informations'embeddedFields: [
  102.         'content.label' => 'Nom',
  103.         'content.description' => 'Description des besoins',
  104.     ])]
  105.     private ?TextContent $content null;
  106.     #[ORM\Column(typeTypes::STRINGlength8)]
  107.     #[Assert\Choice(callback: [CurrencyEnum::class, 'getChoices'], multiplefalse)]
  108.     #[Assert\NotBlank()]
  109.     #[Assert\Length(max8)]
  110.     #[Serial\Groups([
  111.         OpportunitySerializationGroups::DETAIL,
  112.     ])]
  113.     private ?string $currency null;
  114.     #[ORM\Embedded(class: Money::class)]
  115.     #[Assert\Valid()]
  116.     #[TrackField(name""fieldKey'informations'embeddedFields: [
  117.         'estimatedValue.amount' => 'Montant estimé',
  118.     ], callable: 'formatPrice'callableClassNellappFormatService::class)]
  119.     private ?Money $estimatedValue null;
  120.     #[ORM\Column(typeTypes::STRINGlength16)]
  121.     #[Assert\Choice(callback: [OpportunityStatusEnum::class, 'getChoices'], multiplefalse)]
  122.     #[Assert\NotBlank()]
  123.     #[Assert\Length(max16)]
  124.     #[Serial\Groups([
  125.         OpportunitySerializationGroups::DETAIL,
  126.         OpportunitySerializationGroups::COLLECTION,
  127.         OpportunitySerializationGroups::UPDATE_INFORMATION,
  128.     ])]
  129.     #[TrackField(name"Statut de l'opportunité"fieldKey'informations', callable: 'getStatusKey'callableClassOpportunityStatusEnum::class)]
  130.     private ?string $status null;
  131.     #[ORM\Column(typeTypes::STRINGlength24nullabletrue)]
  132.     #[Serial\Groups([
  133.         OpportunitySerializationGroups::DETAIL,
  134.         OpportunitySerializationGroups::COLLECTION,
  135.         OpportunitySerializationGroups::CREATE,
  136.         OpportunitySerializationGroups::UPDATE,
  137.         OpportunitySerializationGroups::UPDATE_CURRENT_STEP,
  138.     ])]
  139.     private ?string $currentStep null;
  140.     #[ORM\Column(typeTypes::DATE_IMMUTABLEnullabletrue)]
  141.     #[Serial\Groups([
  142.         OpportunitySerializationGroups::DETAIL,
  143.         OpportunitySerializationGroups::UPDATE_TRAINING,
  144.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  145.     ])]
  146.     #[TrackField(name"Date prévisionnelle"fieldKey'informations', callable: 'format'callableParams: ['d/m/Y'])]
  147.     private ?DateTimeInterface $estimatedAt null;
  148.     #[ORM\Column(typeTypes::INTEGERnullabletrue)]
  149.     #[Assert\Choice(callback: [CursusModalityEnum::class, 'getChoices'], multiplefalse)]
  150.     #[Assert\NotBlank(groups: [
  151.         OpportunityValidationGroups::UPDATE_TRAINING,
  152.     ])]
  153.     #[Serial\Groups([
  154.         OpportunitySerializationGroups::DETAIL,
  155.         OpportunitySerializationGroups::CREATE,
  156.         OpportunitySerializationGroups::UPDATE_TRAINING,
  157.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  158.     ])]
  159.     #[TrackField(name"Modalité"fieldKey'informations', callable: 'getStatusKey'callableClassCursusModalityEnum::class)]
  160.     private ?int $cursusModality null;
  161.     #[ORM\Column(typeTypes::INTEGERnullabletrue)]
  162.     #[Assert\Choice(callback: [CursusCourseEnum::class, 'getChoices'], multiplefalse)]
  163.     #[Assert\NotBlank(groups: [
  164.         OpportunityValidationGroups::UPDATE_TRAINING,
  165.     ])]
  166.     #[Serial\Groups([
  167.         OpportunitySerializationGroups::DETAIL,
  168.         OpportunitySerializationGroups::CREATE,
  169.         OpportunitySerializationGroups::UPDATE_TRAINING,
  170.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  171.     ])]
  172.     #[TrackField(name"Rythme"fieldKey'informations', callable: 'getStatusKey'callableClassCursusCourseEnum::class)]
  173.     private ?int $cursusCourse null;
  174.     #[ORM\Column(type'integer'nullabletrue)]
  175.     #[Assert\Choice(callback: [RqthStatusEnum::class, 'getChoices'])]
  176.     #[Serial\Groups([
  177.         OpportunitySerializationGroups::DETAIL,
  178.         OpportunitySerializationGroups::CREATE,
  179.         OpportunitySerializationGroups::UPDATE_TRAINING,
  180.     ])]
  181.     #[TrackField(name"Rqth"fieldKey'informations', callable: 'getStatusKey'callableClassRqthStatusEnum::class)]
  182.     private ?int $rqth null;
  183.     #[ORM\ManyToOne(targetEntityPlace::class, fetch'EAGER')]
  184.     #[JoinColumn(nullabletrueonDelete'CASCADE')]
  185.     #[Assert\NotBlank(groups: [
  186.         OpportunityValidationGroups::UPDATE_TRAINING,
  187.     ])]
  188.     #[SerialRelation(targetEntityPlace::class, groups: [
  189.         OpportunitySerializationGroups::UPDATE_TRAINING,
  190.         OpportunitySerializationGroups::DETAIL,
  191.     ])]
  192.     #[TrackField(name"Lieu de rattachement"fieldKey'informations')]
  193.     private ?Place $place null;
  194.     #[ORM\ManyToOne(targetEntityPlace::class, fetch'EAGER')]
  195.     #[JoinColumn(nullabletrueonDelete'CASCADE')]
  196.     #[SerialRelation(targetEntityPlace::class, groups: [
  197.         OpportunitySerializationGroups::UPDATE_TRAINING,
  198.         OpportunitySerializationGroups::DETAIL,
  199.     ])]
  200.     #[TrackField(name"Centre d'examen"fieldKey'informations')]
  201.     private ?Place $examPlace null;
  202.     #[ORM\ManyToOne(targetEntityReferralSource::class)]
  203.     #[JoinColumn(nullabletrueonDelete'CASCADE')]
  204.     #[Assert\NotBlank(groups: [
  205.         OpportunityValidationGroups::UPDATE_INFORMATION,
  206.     ])]
  207.     #[SerialRelation(targetEntityReferralSource::class, groups: [
  208.         OpportunitySerializationGroups::UPDATE_INFORMATION,
  209.         OpportunitySerializationGroups::DETAIL,
  210.     ])]
  211.     #[TrackField(name"Source de l'opportunité"fieldKey'informations')]
  212.     private ?ReferralSource $referralSource null;
  213.     #[ORM\ManyToOne(targetEntityUser::class)]
  214.     #[JoinColumn(nullabletrueonDelete'SET NULL')]
  215.     #[Assert\NotBlank(groups: [
  216.         OpportunityValidationGroups::UPDATE_INFORMATION,
  217.     ])]
  218.     #[SerialRelation(targetEntityUser::class, groups: [
  219.         OpportunitySerializationGroups::UPDATE_INFORMATION,
  220.         OpportunitySerializationGroups::DETAIL,
  221.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  222.     ])]
  223.     #[Serial\Groups([
  224.         OpportunitySerializationGroups::API_COLLECTION,
  225.     ])]
  226.     #[TrackField(name"Propriétaire"fieldKey'informations')]
  227.     private ?User $owner null;
  228.     #[ORM\ManyToOne(targetEntityCompany::class, inversedBy'opportunities')]
  229.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  230.     #[SerialRelation(targetEntityCompany::class, groups: [
  231.         OpportunitySerializationGroups::CREATE,
  232.         OpportunitySerializationGroups::UPDATE_TARGET,
  233.         OpportunitySerializationGroups::DETAIL,
  234.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  235.     ])]
  236.     private ?Company $targetCompany null;
  237.     #[ORM\ManyToOne(targetEntityChannelContact::class, inversedBy'opportunities')]
  238.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  239.     #[SerialRelation(targetEntityChannelContact::class, groups: [
  240.         OpportunitySerializationGroups::CREATE,
  241.         OpportunitySerializationGroups::UPDATE_TARGET,
  242.         OpportunitySerializationGroups::DETAIL,
  243.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  244.     ])]
  245.     private ?ChannelContact $targetContact null;
  246.     #[ORM\OneToMany(mappedBy'opportunity'targetEntityOpportunityChannelContact::class, cascade: ['persist''remove'], orphanRemovaltrue)]
  247.     private Collection $relatedContacts;
  248.     /** @var Collection<int, ChannelContact> */
  249.     #[SerialRelation(targetEntityChannelContact::class, multipletruegroups: [
  250.         OpportunitySerializationGroups::CREATE,
  251.         OpportunitySerializationGroups::DETAIL,
  252.         OpportunitySerializationGroups::UPDATE_TARGET,
  253.     ])]
  254.     private Collection $referents;
  255.     /** @var Collection<int, ChannelContact> */
  256.     #[SerialRelation(targetEntityChannelContact::class, multipletruegroups: [
  257.         OpportunitySerializationGroups::CREATE,
  258.         OpportunitySerializationGroups::DETAIL,
  259.         OpportunitySerializationGroups::UPDATE_TARGET,
  260.     ])]
  261.     private Collection $recipients;
  262.     #[ORM\ManyToOne(targetEntityTraining::class)]
  263.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  264.     #[Assert\NotBlank(groups: [
  265.         OpportunityValidationGroups::UPDATE_TRAINING,
  266.     ])]
  267.     #[SerialRelation(targetEntityTraining::class, groups: [
  268.         OpportunitySerializationGroups::CREATE,
  269.         OpportunitySerializationGroups::DETAIL,
  270.         OpportunitySerializationGroups::UPDATE_TRAINING,
  271.     ])]
  272.     #[TrackField(name"Formation"fieldKey'informations')]
  273.     private ?Training $training null;
  274.     #[ORM\ManyToOne(targetEntityPipeline::class, inversedBy'opportunities')]
  275.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  276.     #[Assert\NotBlank(groups: [
  277.         OpportunityValidationGroups::UPDATE_INFORMATION,
  278.     ])]
  279.     #[SerialRelation(targetEntityPipeline::class, groups: [
  280.         OpportunitySerializationGroups::CREATE,
  281.         OpportunitySerializationGroups::DETAIL,
  282.         OpportunitySerializationGroups::UPDATE_INFORMATION,
  283.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  284.     ])]
  285.     #[TrackField(name"Pipeline"fieldKey'informations')]
  286.     private ?Pipeline $pipeline null;
  287.     #[ORM\ManyToOne(targetEntityPipelineStep::class, inversedBy'opportunities')]
  288.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  289.     #[Assert\NotBlank(groups: [
  290.         OpportunityValidationGroups::UPDATE_INFORMATION,
  291.     ])]
  292.     #[SerialRelation(targetEntityPipelineStep::class, groups: [
  293.         OpportunitySerializationGroups::CREATE,
  294.         OpportunitySerializationGroups::DETAIL,
  295.         OpportunitySerializationGroups::UPDATE_INFORMATION,
  296.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  297.     ])]
  298.     #[TrackField(name"Étape du pipeline"fieldKey'informations')]
  299.     private ?PipelineStep $pipelineStep null;
  300.     #[ORM\Column(typeTypes::DATETIME_IMMUTABLEnullabletrue)]
  301.     #[Serial\Groups([
  302.         OpportunitySerializationGroups::DETAIL,
  303.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  304.     ])]
  305.     private ?DateTimeInterface $pipelineStepSetAt null;
  306.     #[ORM\OneToMany(mappedBy'opportunity'targetEntityQuote::class)]
  307.     //    #[TrackField(name: "Devis", fieldKey: 'quote')]
  308.     private Collection $quotes;
  309.     #[ORM\OneToOne(cascade: ['persist''remove'])]
  310.     private ?Drive $drive null;
  311.     #[Orm\OneToOne(targetEntityActivityFeed::class, cascade: ['persist''remove'], fetch'EXTRA_LAZY'orphanRemovaltrue)]
  312.     private ?ActivityFeed $activityFeed null;
  313.     public function __construct()
  314.     {
  315.         $this->content = new TextContent();
  316.         $this->estimatedValue = new Money($this->currency);
  317.         $this->relatedContacts = new ArrayCollection();
  318.         $this->quotes = new ArrayCollection();
  319.     }
  320.     #[Serial\Groups([
  321.         OpportunitySerializationGroups::DETAIL,
  322.         OpportunitySerializationGroups::COLLECTION,
  323.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  324.         OpportunitySerializationGroups::API_COLLECTION,
  325.     ])]
  326.     public function getContentLabel(): ?string
  327.     {
  328.         return $this->getLineContextLabel();
  329.     }
  330.     #[Serial\Groups([
  331.         OpportunitySerializationGroups::UPDATE_INFORMATION,
  332.         OpportunitySerializationGroups::CREATE,
  333.     ])]
  334.     public function setContentLabel(?string $label): static
  335.     {
  336.         $this->setLineContextLabel($label);
  337.         return $this;
  338.     }
  339.     #[Serial\Groups([
  340.         OpportunitySerializationGroups::DETAIL,
  341.     ])]
  342.     public function getContentDescription(): ?string
  343.     {
  344.         return $this->getLineContextDescription();
  345.     }
  346.     #[Serial\Groups([
  347.         OpportunitySerializationGroups::UPDATE_TRAINING,
  348.         OpportunitySerializationGroups::CREATE,
  349.     ])]
  350.     public function setContentDescription(?string $description): static
  351.     {
  352.         $this->setLineContextDescription($description);
  353.         return $this;
  354.     }
  355.     #[Serial\Groups([
  356.         OpportunitySerializationGroups::DETAIL,
  357.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  358.     ])]
  359.     public function getEstimatedValueAmount(): ?int
  360.     {
  361.         return $this->estimatedValue?->getAmount() ?? null;
  362.     }
  363.     #[Serial\Groups([
  364.         OpportunitySerializationGroups::UPDATE_TRAINING,
  365.     ])]
  366.     public function setEstimatedValueAmount(?int $amount): static
  367.     {
  368.         $this->estimatedValue?->setAmount($amount);
  369.         return $this;
  370.     }
  371.     #[Serial\Groups([
  372.         OpportunitySerializationGroups::DETAIL,
  373.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  374.     ])]
  375.     public function getEstimatedValueCurrency(): ?string
  376.     {
  377.         return $this->estimatedValue?->getCurrency() ?? null;
  378.     }
  379.     #[Serial\Groups([
  380.         OpportunitySerializationGroups::UPDATE_TRAINING,
  381.     ])]
  382.     public function setEstimatedValueCurrency(?string $currency): static
  383.     {
  384.         if (
  385.             null === $currency
  386.             && null === $this->getEstimatedValueAmount()
  387.         ) {
  388.             $this->estimatedValue?->setCurrency($currency);
  389.             return $this;
  390.         }
  391.         $this->estimatedValue?->setCurrency($currency ?? $this->currency ?? CurrencyEnum::default());
  392.         return $this;
  393.     }
  394.     #[Serial\Groups([
  395.         OpportunitySerializationGroups::DETAIL,
  396.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  397.         OpportunitySerializationGroups::API_COLLECTION,
  398.     ])]
  399.     public function getTrainingLabel(): ?string
  400.     {
  401.         return $this->training?->getName();
  402.     }
  403.     #[Serial\Groups([
  404.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  405.     ])]
  406.     public function getTargetLabel(): ?string
  407.     {
  408.         return match ($this->getTargetType()) {
  409.             TargetTypeEnum::COMPANY => $this->targetCompany?->getDisplayName(),
  410.             TargetTypeEnum::CONTACT => $this->getTargetContact()?->getDisplayName(),
  411.             default => null,
  412.         };
  413.     }
  414.     #[Serial\Groups([
  415.         OpportunitySerializationGroups::DETAIL,
  416.         OpportunitySerializationGroups::API_COLLECTION,
  417.     ])]
  418.     public function getPipelineLabel(): ?string
  419.     {
  420.         return $this->pipeline?->getContentLabel();
  421.     }
  422.     #[Serial\Groups([
  423.         OpportunitySerializationGroups::DETAIL,
  424.         OpportunitySerializationGroups::API_COLLECTION,
  425.     ])]
  426.     public function getPipelineStepLabel(): ?string
  427.     {
  428.         return $this->pipelineStep?->getLabel();
  429.     }
  430.     #[Serial\Groups([
  431.         OpportunitySerializationGroups::DETAIL,
  432.         OpportunitySerializationGroups::API_COLLECTION,
  433.     ])]
  434.     public function getPipelineStepPercentage(): ?int
  435.     {
  436.         return $this->pipelineStep?->getPercentage();
  437.     }
  438.     public function getTargetName(): string
  439.     {
  440.         return $this->getTarget()?->getDisplayName() ?? '(inconnu)';
  441.     }
  442.     #[Serial\Groups([
  443.         OpportunitySerializationGroups::DETAIL,
  444.         OpportunitySerializationGroups::COLLECTION,
  445.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  446.         OpportunitySerializationGroups::API_COLLECTION,
  447.     ])]
  448.     public function getDisplayLabel(): string
  449.     {
  450.         return $this->getContentLabel() ?? sprintf('OPPORTUNITÉ du %s pour %s',
  451.             $this->getCreatedAt()->format('d/m/Y'),
  452.             $this->getTargetName(),
  453.         );
  454.     }
  455.     public function __toString()
  456.     {
  457.         return $this->getDisplayLabel();
  458.     }
  459.     public function createRelatedContact(): ChannelContactRelationInterface
  460.     {
  461.         return (new OpportunityChannelContact())
  462.             ->setOpportunity($this)
  463.         ;
  464.     }
  465.     public function getStatusEnumClass(): string
  466.     {
  467.         return OpportunityStatusEnum::class;
  468.     }
  469.     #[Serial\Groups([
  470.         OpportunitySerializationGroups::DETAIL,
  471.         OpportunitySerializationGroups::COLLECTION,
  472.     ])]
  473.     public function isOpen(): bool
  474.     {
  475.         return $this->status === OpportunityStatusEnum::OPEN;
  476.     }
  477.     #[Serial\Groups([
  478.         OpportunitySerializationGroups::DETAIL,
  479.         OpportunitySerializationGroups::COLLECTION,
  480.     ])]
  481.     public function isEditable(): bool
  482.     {
  483.         return false === $this->isArchived();
  484.         // return $this->isOpen();
  485.     }
  486.     #[Serial\Groups([
  487.         OpportunitySerializationGroups::DETAIL,
  488.         OpportunitySerializationGroups::COLLECTION,
  489.     ])]
  490.     public function isWon(): bool
  491.     {
  492.         return $this->status === OpportunityStatusEnum::WON;
  493.     }
  494.     #[Serial\Groups([
  495.         OpportunitySerializationGroups::DETAIL,
  496.         OpportunitySerializationGroups::COLLECTION,
  497.     ])]
  498.     public function isLost(): bool
  499.     {
  500.         return $this->status === OpportunityStatusEnum::LOST;
  501.     }
  502.     #[Serial\Groups([
  503.         OpportunitySerializationGroups::DETAIL,
  504.         OpportunitySerializationGroups::COLLECTION,
  505.     ])]
  506.     public function isClosed(): bool
  507.     {
  508.         return $this->isWon() || $this->isLost();
  509.     }
  510.     public function closeToWon(): static
  511.     {
  512.         $this->status OpportunityStatusEnum::WON;
  513.         return $this;
  514.     }
  515.     public function closeToLost(): static
  516.     {
  517.         $this->status OpportunityStatusEnum::LOST;
  518.         return $this;
  519.     }
  520.     public function reopen(): static
  521.     {
  522.         $this->status OpportunityStatusEnum::OPEN;
  523.         return $this;
  524.     }
  525.     public function initializePipelineStepSetAtValue(): static
  526.     {
  527.         $this->pipelineStepSetAt = new DateTimeImmutable();
  528.         return $this;
  529.     }
  530.     public function resetPipelineStepSetAt(): static
  531.     {
  532.         $this->pipelineStepSetAt null;
  533.         return $this;
  534.     }
  535.     public static function getPermissionResourceClass(): ?string
  536.     {
  537.         return null;
  538.     }
  539.     public function getOwnerChannel(): ?ChannelInterface
  540.     {
  541.         return $this->channel;
  542.     }
  543.     public function getId(): ?int
  544.     {
  545.         return $this->id;
  546.     }
  547.     public function getChannel(): ?Channel
  548.     {
  549.         return $this->channel;
  550.     }
  551.     public function setChannel(?Channel $channel): static
  552.     {
  553.         $this->channel $channel;
  554.         return $this;
  555.     }
  556.     public function getContent(): ?TextContent
  557.     {
  558.         return $this->content;
  559.     }
  560.     public function setContent(?TextContent $content): static
  561.     {
  562.         $this->content $content;
  563.         return $this;
  564.     }
  565.     public function getCurrency(): ?string
  566.     {
  567.         return $this->currency;
  568.     }
  569.     public function setCurrency(?string $currency): static
  570.     {
  571.         $this->currency $currency;
  572.         return $this;
  573.     }
  574.     public function getEstimatedValue(): ?Money
  575.     {
  576.         return $this->estimatedValue;
  577.     }
  578.     public function setEstimatedValue(?Money $estimatedValue): static
  579.     {
  580.         $this->estimatedValue $estimatedValue;
  581.         return $this;
  582.     }
  583.     public function getStatus(): ?string
  584.     {
  585.         return $this->status;
  586.     }
  587.     public function setStatus(?string $status): static
  588.     {
  589.         $this->status $status;
  590.         return $this;
  591.     }
  592.     public function getCurrentStep(): ?string
  593.     {
  594.         return $this->currentStep;
  595.     }
  596.     public function setCurrentStep(?string $currentStep): static
  597.     {
  598.         $this->currentStep $currentStep;
  599.         return $this;
  600.     }
  601.     public function getEstimatedAt(): ?DateTimeInterface
  602.     {
  603.         return $this->estimatedAt;
  604.     }
  605.     public function setEstimatedAt(?DateTimeInterface $estimatedAt): static
  606.     {
  607.         if ($estimatedAt === null) {
  608.             if ($this->estimatedAt === null) {
  609.                 return $this;
  610.             }
  611.             $this->estimatedAt null;
  612.             return $this;
  613.         }
  614.         $newEstimatedAt \DateTimeImmutable::createFromInterface($estimatedAt)
  615.             ->setTime(0000);
  616.         if (
  617.             $this->estimatedAt !== null
  618.             && $this->estimatedAt->format('Y-m-d') === $newEstimatedAt->format('Y-m-d')
  619.         ) {
  620.             return $this;
  621.         }
  622.         $this->estimatedAt $newEstimatedAt;
  623.         return $this;
  624.     }
  625.     public function getCursusModality(): ?int
  626.     {
  627.         return $this->cursusModality;
  628.     }
  629.     public function setCursusModality(?int $cursusModality): static
  630.     {
  631.         $this->cursusModality $cursusModality;
  632.         return $this;
  633.     }
  634.     public function getCursusCourse(): ?int
  635.     {
  636.         return $this->cursusCourse;
  637.     }
  638.     public function setCursusCourse(?int $cursusCourse): static
  639.     {
  640.         $this->cursusCourse $cursusCourse;
  641.         return $this;
  642.     }
  643.     public function getRqth(): ?int
  644.     {
  645.         return $this->rqth;
  646.     }
  647.     public function setRqth(?int $rqth): static
  648.     {
  649.         $this->rqth $rqth;
  650.         return $this;
  651.     }
  652.     public function getPlace(): ?Place
  653.     {
  654.         return $this->place;
  655.     }
  656.     public function setPlace(?Place $place): static
  657.     {
  658.         $this->place $place;
  659.         return $this;
  660.     }
  661.     public function getExamPlace(): ?Place
  662.     {
  663.         return $this->examPlace;
  664.     }
  665.     public function setExamPlace(?Place $examPlace): static
  666.     {
  667.         $this->examPlace $examPlace;
  668.         return $this;
  669.     }
  670.     public function getReferralSource(): ?ReferralSource
  671.     {
  672.         return $this->referralSource;
  673.     }
  674.     public function setReferralSource(?ReferralSource $referralSource): static
  675.     {
  676.         $this->referralSource $referralSource;
  677.         return $this;
  678.     }
  679.     public function getOwner(): ?User
  680.     {
  681.         return $this->owner;
  682.     }
  683.     public function setOwner(?User $owner): static
  684.     {
  685.         $this->owner $owner;
  686.         return $this;
  687.     }
  688.     #[Serial\Groups([
  689.         OpportunitySerializationGroups::DETAIL,
  690.         OpportunitySerializationGroups::PIPELINE_BOARD_DETAIL,
  691.     ])]
  692.     public function getTargetType(): ?string
  693.     {
  694.         $target $this->getTarget();
  695.         return $target === null null
  696.             : match (true) {
  697.                 $target instanceof Company => TargetTypeEnum::COMPANY,
  698.                 $target instanceof ChannelContact => TargetTypeEnum::CONTACT,
  699.                 default => null,
  700.             };
  701.     }
  702.     public function setTarget(?TargetableInterface $target): static
  703.     {
  704.         if ($target !== null) {
  705.             $target->addOpportunity($this);
  706.         } else {
  707.             $this->setTargetCompany(null);
  708.             $this->setTargetContact(null);
  709.         }
  710.         return $this;
  711.     }
  712.     public function setTargetCompany(?Company $targetCompany): static
  713.     {
  714.         $this->targetCompany $targetCompany;
  715.         if ($targetCompany !== null) {
  716.             $this->setTargetContact(null);
  717.         }
  718.         return $this;
  719.     }
  720.     public function setTargetContact(?ChannelContact $targetContact): static
  721.     {
  722.         $this->targetContact $targetContact;
  723.         if ($targetContact !== null) {
  724.             $this->setTargetCompany(null);
  725.         }
  726.         return $this;
  727.     }
  728.     public function getTraining(): ?Training
  729.     {
  730.         return $this->training;
  731.     }
  732.     public function setTraining(?Training $training): static
  733.     {
  734.         $this->training $training;
  735.         return $this;
  736.     }
  737.     public function getPipeline(): ?Pipeline
  738.     {
  739.         return $this->pipeline;
  740.     }
  741.     public function setPipeline(?Pipeline $pipeline): static
  742.     {
  743.         $this->pipeline $pipeline;
  744.         return $this;
  745.     }
  746.     public function getPipelineStep(): ?PipelineStep
  747.     {
  748.         return $this->pipelineStep;
  749.     }
  750.     public function setPipelineStep(?PipelineStep $pipelineStep): static
  751.     {
  752.         $this->pipelineStep $pipelineStep;
  753.         if (null !== $pipelineStep) {
  754.             $this->initializePipelineStepSetAtValue();
  755.         } else {
  756.             $this->resetPipelineStepSetAt();
  757.         }
  758.         return $this;
  759.     }
  760.     public function getPipelineStepSetAt(): ?DateTimeInterface
  761.     {
  762.         return $this->pipelineStepSetAt;
  763.     }
  764.     public function setPipelineStepSetAt(?DateTimeInterface $pipelineStepSetAt): static
  765.     {
  766.         $this->pipelineStepSetAt $pipelineStepSetAt;
  767.         return $this;
  768.     }
  769.     /**
  770.      * @return Collection<int, Quote>
  771.      */
  772.     public function getQuotes(): Collection
  773.     {
  774.         return $this->quotes;
  775.     }
  776.     public function addQuote(Quote $quote): static
  777.     {
  778.         if (!$this->quotes->contains($quote)) {
  779.             $this->quotes->add($quote);
  780.             $quote->setOpportunity($this);
  781.         }
  782.         return $this;
  783.     }
  784.     public function removeQuote(Quote $quote): static
  785.     {
  786.         if ($this->quotes->removeElement($quote)) {
  787.             // set the owning side to null (unless already changed)
  788.             if ($quote->getOpportunity() === $this) {
  789.                 $quote->setOpportunity(null);
  790.             }
  791.         }
  792.         return $this;
  793.     }
  794.     public function getActivityFeed(): ?ActivityFeed
  795.     {
  796.         return $this->activityFeed;
  797.     }
  798.     public function setActivityFeed(?ActivityFeed $activityFeed): Opportunity
  799.     {
  800.         $this->activityFeed $activityFeed;
  801.         return $this;
  802.     }
  803. }