<?php
namespace App\Service;
use App\Enum\DocumentStatusEnum;
use App\Enum\DocumentTypeEnum;
use App\Enum\DocumentTypeOrganizationEnum;
use App\Enum\DocumentTypeUserEnum;
use App\Enum\HeadQuartersEnum;
use App\Enum\InvoiceStatusEnum;
use App\Enum\LegalRepresentativeQualityTypeEnum;
use App\Enum\LegalStatusTypeEnum;
use App\Enum\LetterTypeEnum;
use App\Enum\OrganizationStatusEnum;
use App\Enum\PappersDocumentTypeOrganizationEnum;
use App\Enum\PersonTypeEnum;
use App\Enum\ProductKeyEnum;
use App\Enum\SignatoryEnum;
use App\Traits\SentryNotifyTrait;
use App\Utils\InvoiceUtils;
use App\Utils\QuoteUtils;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Evo\Infrastructure\MappingORM\Document;
use Evo\Infrastructure\MappingORM\LegalRepresentative;
use Evo\Infrastructure\MappingORM\Letter;
use Evo\Infrastructure\MappingORM\Organization;
use Evo\Infrastructure\MappingORM\Person;
use Evo\Infrastructure\MappingORM\Subscription;
use Evo\Infrastructure\PdfGenerator\PathGenerator;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\UrlHelper;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Contracts\Translation\TranslatorInterface;
class OrganizationUtils
{
use SentryNotifyTrait;
public const SERVICE_NAME = '[ORGANIZATION UTILS] :: ';
public const ONE_MONTH = '+1 months';
public const THREE_MONTHS = '+3 months';
public const TWELVE_MONTHS = '+12 months';
private TranslatorInterface $translator;
private UrlHelper $urlHelper;
private DocumentUtils $documentUtils;
private InvoiceUtils $invoiceUtils;
private QuoteUtils $quoteUtils;
private LegalRepresentativeUtils $legalRepresentativeUtils;
private LegalRepresentativeChecker $legalRepresentativeChecker;
private ParameterBagInterface $params;
private GocardlessAPI $gocardlessAPI;
private SessionInterface $session;
private EntityManagerInterface $em;
private UserChecker $userChecker;
private FrontAppAPI $frontAppAPI;
private PersonUtils $personUtils;
private LeadUtils $leadUtils;
private PappersService $pappersService;
private SubscriptionUtils $subscriptionUtils;
private DocumentFilesService $documentFilesService;
public function __construct(
TranslatorInterface $translator,
UrlHelper $urlHelper,
ParameterBagInterface $params,
GocardlessAPI $gocardlessAPI,
SessionInterface $session,
DocumentUtils $documentUtils,
InvoiceUtils $invoiceUtils,
QuoteUtils $quoteUtils,
LegalRepresentativeChecker $legalRepresentativeChecker,
LegalRepresentativeUtils $legalRepresentativeUtils,
LeadUtils $leadUtils,
UserChecker $userChecker,
EntityManagerInterface $em,
PersonUtils $personUtils,
FrontAppAPI $frontAppAPI,
PappersService $pappersService,
SubscriptionUtils $subscriptionUtils,
DocumentFilesService $documentFilesService
) {
$this->translator = $translator;
$this->urlHelper = $urlHelper;
$this->params = $params;
$this->gocardlessAPI = $gocardlessAPI;
$this->session = $session;
$this->documentUtils = $documentUtils;
$this->invoiceUtils = $invoiceUtils;
$this->quoteUtils = $quoteUtils;
$this->legalRepresentativeUtils = $legalRepresentativeUtils;
$this->userChecker = $userChecker;
$this->legalRepresentativeChecker = $legalRepresentativeChecker;
$this->em = $em;
$this->personUtils = $personUtils;
$this->frontAppAPI = $frontAppAPI;
$this->leadUtils = $leadUtils;
$this->pappersService = $pappersService;
$this->subscriptionUtils = $subscriptionUtils;
$this->documentFilesService = $documentFilesService;
}
/**
* @param bool $create
* @param bool $domicile
*
* @deprecated
*/
public function getOrganizationMandatoryDocuments($status, $create = true, $domicile = true): array
{
$mandatoryDocs = [];
if ($create) {
$mandatoryDocs = [
DocumentTypeUserEnum::NIC_RECTO,
DocumentTypeUserEnum::NIC_VERSO,
DocumentTypeUserEnum::NIC_RECTO_VERSO,
DocumentTypeUserEnum::ADDRESS_PROOF,
];
}
$list = [];
foreach ($mandatoryDocs as $docType) {
if (in_array($docType, $mandatoryDocs)) {
$list[$docType] = $this->translator->trans(DocumentTypeOrganizationEnum::getReadableValue($docType));
}
}
return $list;
}
public function checkStatusDomiciliation(Organization $organization): ?string
{
if (!$organization->getCreatedAt()) {
return null;
}
$status = null;
$now = date('Y-m-d');
$dateRegister = $organization->getCreatedAt()->format('Y-m-d');
$domiciliationDate = $organization->getDomiciliationStartDate();
$startingDate = $domiciliationDate ? $domiciliationDate->getTimestamp() : null;
$domiciliationDateTreeMonth = $startingDate ? date('Y-m-d', strtotime(self::THREE_MONTHS, $startingDate)) : null;
$statusException = [
OrganizationStatusEnum::CANCELLED,
OrganizationStatusEnum::PRE_CANCELLATION,
OrganizationStatusEnum::LOST,
OrganizationStatusEnum::DELETED,
];
if (
$domiciliationDateTreeMonth &&
OrganizationStatusEnum::NEW_PAYMENT === $organization->getStatus()
) {
if (
$domiciliationDateTreeMonth < $now &&
(0 === $organization->getBal() || null === $organization->getBal())
) {
return OrganizationStatusEnum::LOST;
}
}
$isPastDomiciliationDate = $domiciliationDate && $domiciliationDate->format('Y-m-d') < $now;
$isNotNewDomiciliationOrInException = !$organization->getIsNewDomiciliation() || in_array($organization->getStatus(), $statusException, true);
if (
($isPastDomiciliationDate && $isNotNewDomiciliationOrInException) ||
(!$domiciliationDate && $isNotNewDomiciliationOrInException)
) {
return null;
}
$documentKBISCancellation = $this->documentUtils->getDocument(DocumentTypeOrganizationEnum::KBIS_CANCELLATION, $organization);
$documentKBISTransfer = $this->documentUtils->getDocument(DocumentTypeOrganizationEnum::KBIS_TRANSFERT, $organization);
if (HeadQuartersEnum::CONTRACT_DOMICILIATION === $organization->getHeadQuartersAddressType()) {
$isKBISCancellationApproved = $documentKBISCancellation && DocumentStatusEnum::APPROVED === $documentKBISCancellation->getStatus();
$isKBISTransferApproved = $documentKBISTransfer && DocumentStatusEnum::APPROVED === $documentKBISTransfer->getStatus();
if ($isKBISCancellationApproved || $isKBISTransferApproved) {
return OrganizationStatusEnum::PRE_CANCELLATION;
}
} elseif ($documentKBISCancellation || $documentKBISTransfer) {
return OrganizationStatusEnum::PRE_CANCELLATION;
}
$haveExpiredDocument = $this->documentUtils->hasExpiredDocument($organization);
if ($haveExpiredDocument &&
in_array($organization->getStatus(), [OrganizationStatusEnum::DOCS_MISSING, OrganizationStatusEnum::OK, OrganizationStatusEnum::DOCS_EXPIRED], true)) {
return OrganizationStatusEnum::DOCS_EXPIRED;
}
$domiciliationContract = $this->documentUtils->getDocument(DocumentTypeOrganizationEnum::DOMICILIATION_CONTRACT, $organization);
$domiciliationCertificate = $this->documentUtils->getDocument(DocumentTypeOrganizationEnum::DOMICILIATION_CERTIFICATE, $organization);
$invoiceRegister = $this->invoiceUtils->checkRegisterInvoice($organization);
$quoteRegister = $this->quoteUtils->checkRegisterQuote($organization);
if ($domiciliationCertificate && DocumentStatusEnum::APPROVED === $domiciliationCertificate->getStatus()) {
if ($this->checkedMissingDocs($organization)) {
$status = OrganizationStatusEnum::OK;
} else {
$status = OrganizationStatusEnum::DOCS_MISSING;
$this->updateInvoiceGenerationDate($organization, false);
}
} else {
$date = date('Y-m-d', strtotime(self::THREE_MONTHS, strtotime($dateRegister)));
if ($date < $now
&& (!$invoiceRegister || $this->invoiceUtils->isFullRepaid($invoiceRegister))
&& !$domiciliationCertificate
&& (!$domiciliationContract || !$domiciliationContract->getSignedBy())) {
$status = OrganizationStatusEnum::LOST;
} elseif (
(($quoteRegister && InvoiceStatusEnum::PAID === $quoteRegister->getInvoice()->getStatus())
|| (
$invoiceRegister && InvoiceStatusEnum::PAID === $invoiceRegister->getStatus()
&& !$domiciliationCertificate
)) && !in_array($organization->getStatus(), $statusException, true)
) {
$status = OrganizationStatusEnum::NEW_PAYMENT;
}
}
if (null !== $status && ($status === $organization->getStatus())) {
$status = null;
}
return $status;
}
public function getGeneratedDocuments(Organization $organization): array
{
$docs = [];
foreach ($organization->getDocuments() as $document) {
if (in_array($document->getType(), $this->getDocsToGenerate($organization), true)) {
$docs[$document->getType()] = [
$this->translator->trans(DocumentTypeOrganizationEnum::getReadableValue($document->getType())) => $this->urlHelper->getAbsoluteUrl($document->getPath()),
];
}
}
return $docs;
}
/**
* Retourne la liste des documents à générer.
*/
public function getDocsToGenerate(Organization $organization, bool $generateContrat = true): array
{
$docs = [];
if (null === $this->getAllLegalRepresentatives($organization)) {
return [];
}
if ($this->documentUtils->checkGenerateDNC($organization, 0)) {
$docs[] = DocumentTypeOrganizationEnum::DNC1;
}
if ($this->documentUtils->checkGenerateDNC($organization, 1)) {
$docs[] = DocumentTypeOrganizationEnum::DNC2;
}
if ($this->documentUtils->checkGenerateHQOwner($organization)) {
$docs[] = DocumentTypeOrganizationEnum::HQ_OWNER;
}
if ($this->documentUtils->checkGeneratePowerOfAttorney($organization)) {
$docs[] = DocumentTypeOrganizationEnum::KOAH_POWER_OF_ATTORNEY;
}
if ($this->documentUtils->checkGenerateSubscribersList($organization)) {
$docs[] = DocumentTypeOrganizationEnum::SUBSCRIBERS_LIST;
}
if ($this->checkGenerateDomiciliationContrat($organization) && $generateContrat) {
$docs[] = DocumentTypeOrganizationEnum::DOMICILIATION_CONTRACT;
}
if ($this->checkGenerateCertificateDomiciliation($organization)) {
$docs[] = DocumentTypeOrganizationEnum::DOMICILIATION_CERTIFICATE;
}
if (in_array($organization->getStatus(), [OrganizationStatusEnum::PRE_CANCELLATION, OrganizationStatusEnum::CANCELLED], true)) {
$docs[] = DocumentTypeOrganizationEnum::NOTICE_OF_TERMINATION;
}
return $docs;
}
public function getOrganizationRIB(Organization $organization)
{
$documents = $organization->getDocuments();
foreach ($documents as $document) {
if (DocumentTypeOrganizationEnum::RIB === $document->getType()) {
return $document;
}
}
return null;
}
/**
* Récupérer les représentants legaux d'une organisation selon son statut juridique.
*/
public function getAllLegalRepresentatives(Organization $organization): ?array
{
$aLegalStatus = $this->legalRepresentativeChecker->getByLegalStatus();
$aLegalRepresentativeTypes = $this->getAvailableLegalRepresentativeTypes($organization->getLegalStatus());
if (!isset($aLegalStatus[$organization->getLegalStatus()])) {
return null;
}
$aLegalRepresentative = [];
foreach ($organization->getLegalRepresentatives() as $legalRepresentative) {
/** @var Person $person */
$person = $legalRepresentative->getPerson();
if ($person) {
$type = $legalRepresentative->getPerson()->getType();
$quality = $legalRepresentative->getQuality();
if ($type &&
$quality &&
in_array($type, $aLegalRepresentativeTypes, true) &&
array_key_exists($quality, $aLegalStatus[$organization->getLegalStatus()]) &&
LegalRepresentativeQualityTypeEnum::ASSOCIE_SEUL !== $quality
) {
$aLegalRepresentative[] = $legalRepresentative;
}
}
}
$this->legalRepresentativeUtils->sortByQuality($aLegalRepresentative);
if (!in_array($organization->getLegalStatus(), [LegalStatusTypeEnum::SAS, LegalStatusTypeEnum::SASU], true)
) {
$aLegalRepresentative = array_reverse($aLegalRepresentative);
}
return (empty($aLegalRepresentative)) ? null : array_values($aLegalRepresentative);
}
/**
* @param string $legalStatus
*/
public function getAvailableLegalRepresentativeTypes($legalStatus): array
{
$aLegalStatus = [
LegalStatusTypeEnum::SAS,
LegalStatusTypeEnum::SASU,
LegalStatusTypeEnum::SCCV,
LegalStatusTypeEnum::SCI,
LegalStatusTypeEnum::CIVIL_ORGANISATION,
LegalStatusTypeEnum::SNC,
];
if (in_array($legalStatus, $aLegalStatus, true)) {
$legalRepresentativeTypes = [
PersonTypeEnum::PHYSICAL,
PersonTypeEnum::LEGAL,
];
} else {
$legalRepresentativeTypes = [
PersonTypeEnum::PHYSICAL,
];
}
return $legalRepresentativeTypes;
}
public function haveValidateDocuments(int $orgaId): ?array
{
$lrRepo = $this->em->getRepository(LegalRepresentative::class);
return $lrRepo->hasDocToValided($orgaId);
}
public function haveKbisCancellationToApprove(int $orgaId): ?array
{
$docRepo = $this->em->getRepository(Document::class);
return $docRepo->hasKbisCancellationToApprove($orgaId);
}
public function haveContratToSign(int $orgaId): ?array
{
$docRepo = $this->em->getRepository(Document::class);
return $docRepo->hasContractSign($orgaId);
}
public function getLegalRepresentativeOfOrganization(Organization $organization): ?Person
{
/** @var ArrayCollection $legalRepresentatives */
$legalRepresentatives = $organization->getLegalRepresentatives();
if ($legalRepresentatives->count() > 0) {
/** @var LegalRepresentative $legalRepresentative */
foreach ($legalRepresentatives as $legalRepresentative) {
$person = $legalRepresentative->getPerson();
if ((null !== $person) && (PersonTypeEnum::PHYSICAL === $person->getType())) {
return $person;
}
}
}
return null;
}
public function checkGenerateDomiciliationContrat(Organization $organization): bool
{
if (!$organization
|| !$organization->getIsNewDomiciliation()
|| !SubscriptionUtils::getDomSubscription($organization)
|| !$organization->getDomiciliationStartDate()
|| 0 == count($organization->getLegalRepresentatives())
|| $this->documentUtils->isSignedDocument(
DocumentTypeOrganizationEnum::DOMICILIATION_CONTRACT,
$organization,
[SignatoryEnum::BOTH, SignatoryEnum::SIGNED_CLIENT]
)
|| OrganizationStatusEnum::DOCS_MISSING === $organization->getStatus()) {
return false;
}
/*
* Checked if domiciliation certificate is approved
*/
foreach ($organization->getDocuments() as $document) {
if (DocumentTypeOrganizationEnum::DOMICILIATION_CERTIFICATE === $document->getType() &&
DocumentStatusEnum::APPROVED === $document->getStatus()) {
return false;
}
}
if ($organization->getAddress() || null !== $organization->getStore()) {
$legalRepresentative = $this->getLegalRepresentativeLoop($organization);
if (!empty($legalRepresentative)) {
$legalRepresentative = $legalRepresentative[0];
if (null === $legalRepresentative->getPhoneNumber()) {
return false;
}
} else {
return false;
}
$subscription = SubscriptionUtils::getDomSubscription($organization);
if (null !== $subscription) {
$pack = $this->subscriptionUtils->getPackUniqueKey($subscription);
if (null !== $pack) {
return true;
}
}
}
return false;
}
public function getLegalRepresentativeLoop(Organization $organization): ?array
{
foreach ($organization->getLegalRepresentatives() as $legalRepresentative) {
if (in_array($legalRepresentative->getQuality(), [LegalRepresentativeQualityTypeEnum::CHAIRMAN, LegalRepresentativeQualityTypeEnum::MANAGER], true)
&& null !== $legalRepresentative->getPerson()
) {
return [$legalRepresentative];
}
}
return null;
}
public function checkGenerateCertificateDomiciliation(Organization $organization): bool
{
return OrganizationStatusEnum::NEW_PAYMENT === $organization->getStatus() &&
$this->checkApprovedDocument($organization) &&
!$this->documentUtils->hasDocument(DocumentTypeOrganizationEnum::DOMICILIATION_CERTIFICATE, $organization);
}
public function checkApprovedDocument(Organization $organization): bool
{
$aDocs = $this->documentUtils->getDocumentsTypes($organization->getDocuments());
if (empty($aDocs)) {
return false;
}
/*
* Vérification si contrat de domiciliation est signé
*/
if (!$this->documentUtils->isSignedDocument(DocumentTypeOrganizationEnum::DOMICILIATION_CONTRACT, $organization)) {
return false;
}
/*
* Validation RIB
*/
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::RIB, $aDocs)) {
return false;
}
$aLegalStatus = [
LegalStatusTypeEnum::EURL,
LegalStatusTypeEnum::SARL,
LegalStatusTypeEnum::SNC,
LegalStatusTypeEnum::AUTO_ENTREPRENEUR,
LegalStatusTypeEnum::ASSOCIATION,
LegalStatusTypeEnum::SCI,
LegalStatusTypeEnum::SCCV,
LegalStatusTypeEnum::CIVIL_ORGANISATION,
LegalStatusTypeEnum::EIRL,
LegalStatusTypeEnum::EI,
LegalStatusTypeEnum::SELARL,
LegalStatusTypeEnum::SDE,
LegalStatusTypeEnum::BL,
];
if (in_array($organization->getLegalStatus(), $aLegalStatus, true)) {
// check document for LR MANAGER et CO MANAGER
$hasLegalRepresentativeWithQualityManager = $this->hasLegalRepresentativeWithQuality(LegalRepresentativeQualityTypeEnum::MANAGER, $organization);
if ($hasLegalRepresentativeWithQualityManager) {
$filter = [LegalRepresentativeQualityTypeEnum::MANAGER, LegalRepresentativeQualityTypeEnum::CO_MANAGER];
if ($this->documentUtils->hasInvalidDocument($organization, $filter)) {
return false;
}
} else {
return false;
}
} elseif (in_array($organization->getLegalStatus(), [LegalStatusTypeEnum::SAS, LegalStatusTypeEnum::SASU], true)) {
// check document for LR CHAIRMAN et GENERAL_MANAGER
$hasLegalRepresentativeWithQualityChairman = $this->hasLegalRepresentativeWithQuality(LegalRepresentativeQualityTypeEnum::CHAIRMAN, $organization);
if ($hasLegalRepresentativeWithQualityChairman) {
$filter = [LegalRepresentativeQualityTypeEnum::CHAIRMAN, LegalRepresentativeQualityTypeEnum::GENERAL_MANAGER];
if ($this->documentUtils->hasInvalidDocument($organization, $filter)) {
return false;
}
}
} else {
return false;
}
return true;
}
public function hasLegalRepresentativeWithQuality($quality, Organization $organization): bool
{
$legalRepresentatives = $this->getAllLegalRepresentatives($organization);
foreach ($legalRepresentatives ?? [] as $legalRepresentative) {
if ($legalRepresentative->getQuality() === $quality) {
return true;
}
}
return false;
}
public function statusChangedActions(string $newStatus, Organization $organization, $flush = false): void
{
switch ($newStatus) {
case OrganizationStatusEnum::CANCELLED:
$this->cancelOrganizationSubscription($organization);
break;
case OrganizationStatusEnum::PRE_CANCELLATION:
if (null === $organization->getTerminationDate()) {
$now = new \DateTime();
$terminationDate = date('Y-m-d', strtotime(self::ONE_MONTH, $now->getTimestamp()));
$organization->setTerminationDate(new \DateTime($terminationDate));
}
break;
case OrganizationStatusEnum::NEW:
$this->leadUtils->changeStatus($organization, false);
break;
}
if ($flush) {
try {
$this->em->persist($organization);
$this->em->flush();
} catch (ORMException $e) {
$this->captureSentryException($e);
}
}
}
public function cancelOrganizationSubscription(Organization $organization): void
{
$subscription = SubscriptionUtils::getDomSubscription($organization);
$this->cancelledSubscriptions($organization);
if (null !== $subscription) {
// Cancelled Gocardless
$this->gocardlessAPI->canceledMandate($organization->getMandatID());
try {
$this->em->persist($subscription);
} catch (ORMException $e) {
$this->captureSentryException($e);
}
}
$organization->setMandatID(null);
$organization->setReferenceGocardless(null);
try {
$this->em->persist($organization);
$this->em->flush();
} catch (ORMException $e) {
$this->captureSentryException($e);
}
}
public function cancelledSubscriptions(Organization $organization): void
{
$subscriptions = $organization->getSubscriptions();
/** @var Subscription $subscription */
foreach ($subscriptions as $subscription) {
if ($subscription->getActive()) {
$subscription->setActive(false);
try {
$this->em->persist($subscription);
$this->em->flush();
} catch (ORMException $e) {
$this->captureSentryException($e);
}
}
}
}
public function getNextGeneratingInvoice(Organization $organization)
{
$domiciliationStartDate = $organization->getDomiciliationStartDate();
if (!$domiciliationStartDate instanceof \DateTimeInterface) {
return null;
}
$subscription = SubscriptionUtils::getDomSubscription($organization);
if (null !== $subscription) {
$nextPayment = $subscription->getNextPayment();
$now = date('Y-m-d');
$domiciliationStartDay = $domiciliationStartDate->format('d');
$frequency = SubscriptionUtils::getFrequency($subscription);
$tmpDate = date('Y-m-d', strtotime($frequency, $domiciliationStartDate->getTimestamp()));
if (null !== $nextPayment) {
$referenceDate = $nextPayment->format('Y-m-d') > $tmpDate ? $nextPayment->format('Y-m-d') : $tmpDate;
} else {
$referenceDate = $tmpDate;
}
if ($now > $referenceDate) {
$referenceDate = $now;
}
$tmp = explode('-', $referenceDate);
if ('02' === $tmp[1] && in_array($domiciliationStartDay, ['28', '29', '30', '31'])) {
$domiciliationStartDay = '27';
}
$reference = $tmp[0].'-'.$tmp[1].'-'.$domiciliationStartDay;
if ($reference < $now) {
$reference = date('Y-m-d', strtotime('+1 month', strtotime($reference)));
}
return $reference;
}
return null;
}
public function getContratDomiciliation(Organization $organization): ?Document
{
$documents = $organization->getDocuments();
/** @var Document $document */
foreach ($documents as $document) {
if (DocumentTypeOrganizationEnum::DOMICILIATION_CONTRACT === $document->getType()) {
return $document;
}
}
return null;
}
/**
* @return bool
*/
public function hasLetterWelcome(Organization $organization)
{
/** @var Letter $letter */
foreach ($organization->getLetters()->toArray() as $letter) {
if (LetterTypeEnum::WELCOME === $letter->getType()) {
return true;
}
}
return false;
}
public function generateOnlyDomiciliationCertificate(Document $document)
{
if (null !== $document->getPerson()) {
$aDocs = [];
$organizations = $this->personUtils->getOrganizations($document->getPerson());
$organizationsOfLRParent = $this->personUtils->getOrganizationsParentOfLR($document->getPerson());
$organizations = array_merge_recursive($organizations, $organizationsOfLRParent);
$organizationIDFinished = [];
/** @var Organization $organization */
foreach ($organizations as $organization) {
if (!in_array($organization->getId(), $organizationIDFinished) && $this->checkGenerateCertificateDomiciliation($organization)) {
$aDocs[] = DocumentTypeOrganizationEnum::DOMICILIATION_CERTIFICATE;
$this->generateDocs($organization, false, $aDocs);
}
}
}
}
/**
* Generate document.
*
* @param array<string> $docsToGenerate
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function generateDocs(
Organization $organization,
bool $generateContract = true,
array $docsToGenerate = [],
bool $isGenerationAuto = false
): ?Document {
if (empty($docsToGenerate)) {
$docsToGenerate = $this->getDocsToGenerate($organization, $generateContract);
}
$docsToGenerate = array_unique($docsToGenerate);
$formalityDocuments = [
DocumentTypeOrganizationEnum::DNC1,
DocumentTypeOrganizationEnum::DNC2,
DocumentTypeOrganizationEnum::KOAH_POWER_OF_ATTORNEY,
DocumentTypeOrganizationEnum::HQ_OWNER,
];
$documents = [];
$organizationTmp = clone $organization;
foreach ($docsToGenerate as $docType) {
if (DocumentTypeEnum::DOMICILIATION_CONTRACT === $docType) {
$this->updateOrganizationLegalName($organization);
}
$status = DocumentStatusEnum::APPROVED;
if (in_array($docType, $formalityDocuments, true)) {
$status = DocumentStatusEnum::GENERATED_DOC;
}
$trans = DocumentTypeOrganizationEnum::getReadableValue($docType);
$document = new Document();
$document
->setType($docType)
->setStatus($status)
->setName($this->translator->trans($trans));
$documents[] = $document;
// just use for checking status domiciliation
$organizationTmp->addDocument($document);
// overwrite the organization of the new document
$document->setOrganization($organization);
}
$organizationStatus = $this->checkStatusDomiciliation($organizationTmp);
// Detach the organizationTmp to avoid the flush
$this->em->detach($organizationTmp);
if ($organizationStatus && $organization->getStatus() !== $organizationStatus) {
$organization->setStatus($organizationStatus);
}
if (!$isGenerationAuto) {
$this->em->flush();
}
return $documents[0] ?? null;
}
/**
* Vérifier les documents manquants si approuvés.
*/
public function checkedMissingDocs(Organization $organization)
{
$aDocs = $this->documentUtils->getDocumentsTypes($organization->getDocuments());
if (empty($aDocs)) {
return false;
}
switch ($organization->getLegalStatus()) {
case LegalStatusTypeEnum::AUTO_ENTREPRENEUR:
case LegalStatusTypeEnum::EI:
case LegalStatusTypeEnum::EIRL:
case LegalStatusTypeEnum::ASSOCIATION:
if (LegalStatusTypeEnum::ASSOCIATION === $organization->getLegalStatus()) {
$document = DocumentTypeOrganizationEnum::ASSOCIATION_RECEIPT;
} else {
$document = DocumentTypeOrganizationEnum::MICRO_ORGANIZATION_RECEIPT;
}
if (!$this->documentUtils->isApprovedDocument($document, $aDocs)) {
return false;
}
break;
case LegalStatusTypeEnum::SDE:
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::STATUS_TRADUITS_SOCIETE_MERE, $aDocs)) {
return false;
}
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::KBIS_SUCCURSALE, $aDocs)) {
return false;
}
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::DBE, $aDocs)) {
return false;
}
break;
case LegalStatusTypeEnum::BL:
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::STATUS_TRADUITS_SOCIETE_MERE, $aDocs)) {
return false;
}
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::KBIS_SOCIETE_MERE, $aDocs)) {
return false;
}
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::DBE, $aDocs)) {
return false;
}
break;
default:
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::STATUS, $aDocs)) {
return false;
}
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::KBIS, $aDocs)) {
return false;
}
if (!$this->documentUtils->isApprovedDocument(DocumentTypeOrganizationEnum::DBE, $aDocs)) {
return false;
}
}
return true;
}
public function hasAllRequiredDocumentToPassToOKStatus(Organization $organization): bool
{
$requiredDocuments = $this->getRequiredDocumentsTypeToPassToOKStatus($organization);
$aDocs = $this->documentUtils->getDocumentsTypes($organization->getDocuments());
foreach ($requiredDocuments as $document) {
if (!array_key_exists($document, $aDocs)) {
return false;
}
}
return true;
}
public function hasAllDocumentsUploadedButAtLeastOneRefused(Organization $organization): bool
{
$aDocs = $this->documentUtils->getDocumentsTypes($organization->getDocuments());
$requiredDocuments = $this->getRequiredDocumentsTypeToPassToOKStatus($organization);
$docsToChecks = array_intersect_key($aDocs, array_flip($requiredDocuments));
$countNotApprovedDoc = 0;
foreach ($requiredDocuments as $requiredDocument) {
if ($this->documentUtils->isRefusedDocument($requiredDocument, $docsToChecks)) {
++$countNotApprovedDoc;
}
}
return $countNotApprovedDoc >= 1;
}
public function getRequiredDocumentsTypeToPassToOKStatus(Organization $organization): array
{
$legalStatus = $organization->getLegalStatus();
switch ($legalStatus) {
case LegalStatusTypeEnum::AUTO_ENTREPRENEUR:
case LegalStatusTypeEnum::EI:
case LegalStatusTypeEnum::EIRL:
case LegalStatusTypeEnum::ASSOCIATION:
if (LegalStatusTypeEnum::ASSOCIATION === $legalStatus) {
return [DocumentTypeOrganizationEnum::ASSOCIATION_RECEIPT];
}
return [DocumentTypeOrganizationEnum::MICRO_ORGANIZATION_RECEIPT];
case LegalStatusTypeEnum::SDE:
return [
DocumentTypeOrganizationEnum::STATUS_TRADUITS_SOCIETE_MERE,
DocumentTypeOrganizationEnum::KBIS_SUCCURSALE,
DocumentTypeOrganizationEnum::DBE,
];
case LegalStatusTypeEnum::BL:
return [
DocumentTypeOrganizationEnum::STATUS_TRADUITS_SOCIETE_MERE,
DocumentTypeOrganizationEnum::KBIS_SOCIETE_MERE,
DocumentTypeOrganizationEnum::DBE,
];
default:
return [
DocumentTypeOrganizationEnum::STATUS,
DocumentTypeOrganizationEnum::KBIS,
DocumentTypeOrganizationEnum::DBE,
];
}
}
public function getMissingDocsForPappers(Organization $organization): array
{
$aDocs = $this->documentUtils->getDocumentsTypes($organization->getDocuments());
// Remove not approved documents
$aDocs = array_filter($aDocs, static function ($doc) {
return DocumentStatusEnum::NOT_APPROVED !== $doc;
});
$missingDocuments = [];
if (empty($aDocs)) {
return $missingDocuments;
}
$legalStatus = $organization->getLegalStatus();
$requiredDocumentTypes = [
DocumentTypeOrganizationEnum::KBIS,
DocumentTypeOrganizationEnum::DBE,
DocumentTypeOrganizationEnum::STATUS,
];
switch ($legalStatus) {
case LegalStatusTypeEnum::AUTO_ENTREPRENEUR:
case LegalStatusTypeEnum::EI:
case LegalStatusTypeEnum::EIRL:
case LegalStatusTypeEnum::ASSOCIATION:
if (LegalStatusTypeEnum::ASSOCIATION === $organization->getLegalStatus()) {
$requiredDocumentTypes = [DocumentTypeOrganizationEnum::ASSOCIATION_RECEIPT];
} else {
$requiredDocumentTypes = [DocumentTypeOrganizationEnum::MICRO_ORGANIZATION_RECEIPT];
}
break;
case LegalStatusTypeEnum::SDE:
$requiredDocumentTypes = [
DocumentTypeOrganizationEnum::KBIS_SUCCURSALE,
DocumentTypeOrganizationEnum::DBE,
DocumentTypeOrganizationEnum::STATUS_TRADUITS_SOCIETE_MERE,
];
break;
case LegalStatusTypeEnum::BL:
$requiredDocumentTypes = [
DocumentTypeOrganizationEnum::KBIS_SOCIETE_MERE,
DocumentTypeOrganizationEnum::DBE,
DocumentTypeOrganizationEnum::STATUS_TRADUITS_SOCIETE_MERE,
];
break;
}
foreach ($requiredDocumentTypes as $documentType) {
if (!array_key_exists($documentType, $aDocs)) {
$missingDocuments[] = $documentType;
}
}
return $missingDocuments;
}
/**
* Récupère les représentants légaux PHYSIQUES de niveau le plus haut (CHAIRMAN pour SAS/SASU, MANAGER pour SARL/EURL).
*
* @return array|null
*/
public function getPhysicalLegalRepresentativeLoop(Organization $organization)
{
foreach ($organization->getLegalRepresentatives() as $legalRepresentative) {
if (in_array(
$legalRepresentative->getQuality(),
[LegalRepresentativeQualityTypeEnum::CHAIRMAN, LegalRepresentativeQualityTypeEnum::MANAGER]
)
&& null !== $legalRepresentative->getPerson()
) {
if (PersonTypeEnum::PHYSICAL == $legalRepresentative->getPerson()->getType()) {
return [$legalRepresentative];
}/* else {
return $this->getFirstPhysicalLR($legalRepresentative);
}*/
}
}
return null;
}
public function getFirstPhysicalLR(LegalRepresentative $legalRepresentative)
{
if ($legalRepresentative->getPerson() && PersonTypeEnum::PHYSICAL == $legalRepresentative->getPerson()->getType()) {
return $legalRepresentative;
} elseif (null !== $legalRepresentative->getParent()) {
return $this->getFirstPhysicalLR($legalRepresentative->getParent());
}
return null;
}
/**
* @return mixed
*/
public function generateURLGocardLess(Organization $organization, ?string $source = null)
{
$sessionID = $this->generateToken();
$this->session->set('session_id', $sessionID);
if ('TUNNEL' === $source) {
$redirectTo = $this->params->get('uri_tunnel').'/documents/'.$organization->getId().'?rib=ok&session_id='.$sessionID;
} else {
$redirectTo = $this->params->get('uri_bv').'/validation-gc/EDIT/'.$source.'?organization='.$organization->getId().'&session_id='.$sessionID;
}
$redirectFlows = $this->gocardlessAPI->createRedirectFlows($sessionID, $redirectTo);
if (null !== $redirectFlows) {
return $redirectFlows->redirect_url;
}
return null;
}
private function generateToken()
{
return 'SESS_'.rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
}
/**
* @return array
*/
public function getMandatoryDocuments(Organization $organization)
{
$mandatoryDocuments = [
DocumentTypeOrganizationEnum::RIB,
];
if (in_array(
$organization->getLegalStatus(),
[
LegalStatusTypeEnum::EI,
LegalStatusTypeEnum::AUTO_ENTREPRENEUR,
LegalStatusTypeEnum::EIRL,
LegalStatusTypeEnum::ASSOCIATION,
]
)) {
if (LegalStatusTypeEnum::ASSOCIATION == $organization->getLegalStatus()) {
$mandatoryDocuments[] = DocumentTypeOrganizationEnum::ASSOCIATION_RECEIPT;
} else {
$mandatoryDocuments[] = DocumentTypeOrganizationEnum::MICRO_ORGANIZATION_RECEIPT;
}
} else {
$mandatoryDocuments[] = DocumentTypeOrganizationEnum::STATUS;
$mandatoryDocuments[] = DocumentTypeOrganizationEnum::DBE;
$mandatoryDocuments[] = DocumentTypeOrganizationEnum::KBIS;
}
$list = [];
foreach ($mandatoryDocuments as $mandatoryDocument) {
$list[$mandatoryDocument] = $this->translator->trans(DocumentTypeOrganizationEnum::getReadableValue($mandatoryDocument));
}
return $list;
}
public function hasReexpedition(Organization $organization): bool
{
$subscription = SubscriptionUtils::getDomSubscription($organization);
if (null !== $subscription) {
$products = $subscription->getProductsFromServices();
foreach ($products as $product) {
if (ProductKeyEnum::REEXPEDITION_COURRIER === $product->getUniqueKey() || ProductKeyEnum::REEXPEDITION_TRIMESTRIELLE === $product->getUniqueKey()) {
return true;
}
}
}
return false;
}
public function checkPermission(Organization $organization, $token)
{
$user = $this->userChecker->checkUserByToken($token);
if (!$user) {
return false;
}
return $organization && (in_array($user, $organization->getUsers())
|| in_array('ROLE_ADMIN', $user->getRoles()));
}
private function updateInvoiceGenerationDate(Organization $organization, bool $reset = false): void
{
$subscription = SubscriptionUtils::getDomSubscription($organization);
if ($subscription && $organization->getDomiciliationStartDate()) {
$frequency = SubscriptionUtils::getFrequency($subscription);
$nextPayment = date('Y-m-d', strtotime($frequency, $organization->getDomiciliationStartDate()->getTimestamp()));
if (!$subscription->getInvoiceGenerationDate() || $reset) {
$subscription->setInvoiceGenerationDate($organization->getDomiciliationStartDate());
$subscription->setNextPayment(new \DateTime($nextPayment));
$this->em->persist($subscription);
$this->em->flush();
}
}
}
public function updateInfoFrontApp(Organization $organization): void
{
$appEnv = getenv('APP_ENV');
if ('prod' === $appEnv && [] !== $organization->getUsers()) {
$user = $organization->getUsers()[0];
$this->frontAppAPI->updateContact($user);
}
}
/**
* Get category.
*/
public static function getCategory(Organization $organization): string
{
$category = [];
if ($organization->getIsNewDomiciliation()) {
$category[] = 'Domiciliation';
}
if ($organization->getIsNewImmatriculation()) {
$category[] = 'IMMAT';
}
if ($organization->getIsNewTransfert()) {
$category[] = 'Transfert';
}
return implode(' + ', $category);
}
public function generateDocsFromPappers(Organization $organization, Document $document): bool
{
$documentsToGenerate = PappersDocumentTypeOrganizationEnum::DOCUMENT_PAPPERS_ABLE_TO_GENERATE;
$type = array_search($document->getType(), $documentsToGenerate, true);
if ($organization->getSIRET() && $document->getType()) {
$response = $this->pappersService->getDocument((string) $organization->getSIRET(), $type);
if (200 === $response->getStatusCode()) {
$this->documentFilesService->writeFile(
PathGenerator::generatePathForDocument($document),
$response->getContent()
);
return true;
}
$response = json_decode($response->getContent(), null, 512, JSON_THROW_ON_ERROR);
throw new HttpException($response->statusCode, sprintf('Error %s: %s', $response->statusCode, $response->message));
}
return false;
}
public function updateSurveillance(Organization $organization, ?array $sirenChangeset)
{
$organizationWithSameSiren = $this->em->getRepository(Organization::class)->findOneBy(['SIRET' => $organization->getSIRET()]);
$organizationWithSameOldSiren = null;
if ($sirenChangeset && $sirenChangeset[0]) {
$organizationWithSameOldSiren = $this->em->getRepository(Organization::class)->findOneBy(['SIRET' => $sirenChangeset[0]]);
}
$case1 = $this->getCaseForSurveillance(1, $organization, $sirenChangeset);
$case2 = $this->getCaseForSurveillance(2, $organization, $sirenChangeset);
$case3 = $this->getCaseForSurveillance(3, $organization, $sirenChangeset);
$case4 = $this->getCaseForSurveillance(4, $organization, $sirenChangeset);
$mustUpdate = true;
switch (true) {
case $case1:
$organization->setIsSurveillance(true);
$this->pappersService->addSurveillance(['siren' => $organization->getSIRET()]);
break;
case $case2:
$organization->setIsSurveillance(false);
if ($sirenChangeset && $sirenChangeset[0] && !$organizationWithSameOldSiren) {
$this->pappersService->removeSurveillance(['siren' => $sirenChangeset[0]]);
}
break;
case $case3:
if ($sirenChangeset && $sirenChangeset[0] && !$organizationWithSameOldSiren) {
$this->pappersService->removeSurveillance(['siren' => $sirenChangeset[0]]);
} else {
$mustUpdate = false;
}
$this->pappersService->addSurveillance(['siren' => $organization->getSIRET()]);
break;
case $case4:
$organization->setIsSurveillance(false);
if (!$organizationWithSameSiren || $organizationWithSameSiren->getId() === $organization->getId()) {
$this->pappersService->removeSurveillance(['siren' => $organization->getSIRET()]);
}
break;
default:
$mustUpdate = false;
break;
}
if ($mustUpdate) {
$this->em->flush();
}
}
private function getCaseForSurveillance(
int $case,
Organization $organization,
?array $sirenChangeset
): bool {
$isInActiveStatus = in_array($organization->getStatus(), [
OrganizationStatusEnum::DOCS_MISSING,
OrganizationStatusEnum::OK,
OrganizationStatusEnum::DOCS_EXPIRED,
], true);
$isInLostStatus = in_array($organization->getStatus(), [
OrganizationStatusEnum::CANCELLED,
OrganizationStatusEnum::LOST,
OrganizationStatusEnum::DELETED,
], true);
$isInLegalStatus = in_array($organization->getLegalStatus(), LegalStatusTypeEnum::$legalStatusForSurveillance, true);
switch ($case) {
case 1:
return !in_array(
false,
[
$isInActiveStatus,
!$organization->getIsSurveillance(),
!empty($organization->getSIRET()),
$isInLegalStatus,
],
true
);
case 2:
return $sirenChangeset && !in_array(
false,
[
$isInActiveStatus,
$organization->getIsSurveillance(),
$sirenChangeset,
$sirenChangeset[0],
empty($organization->getSIRET()),
$isInLegalStatus,
],
true
);
case 3:
return $sirenChangeset && !in_array(
false,
[
$isInActiveStatus,
$organization->getIsSurveillance(),
$sirenChangeset,
$sirenChangeset[0],
!empty($organization->getSIRET()),
$isInLegalStatus,
],
true
);
default:
return !in_array(
false,
[
$isInLostStatus,
$organization->getIsSurveillance(),
!empty($organization->getSIRET()),
],
true
);
}
}
public function updateOrganizationLegalName(Organization $organization): void
{
if (
!$organization->getLegalName() &&
$organization->getLegalRepresentatives()->count() > 0 &&
in_array(
$organization->getLegalStatus(),
[LegalStatusTypeEnum::EI, LegalStatusTypeEnum::AUTO_ENTREPRENEUR]
)
) {
/** @var LegalRepresentative $legalRepresentative */
$legalRepresentative = $organization->getLegalRepresentatives()->filter(
fn (LegalRepresentative $lr) => LegalRepresentativeQualityTypeEnum::MANAGER === $lr->getQuality()
)->first();
if (!$legalRepresentative) {
return;
}
$this->em->getRepository(Organization::class)->update(
$organization,
['legalName' => $legalRepresentative->getPerson()->getFullName()]
);
}
}
}