- Add nullable debt field to User entity with addDebt/reduceDebt helpers - On refund webhook: add refunded amount to organizer debt - On dispute webhook (charge.dispute.created): add disputed amount to debt - OrderController: if organizer has debt > 0, payment goes to main Stripe account instead of connected account, debt reduced on payment success - Display debt amount on organizer dashboard with warning message - Add dispute notification email template - Migration for debt column on user table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
720 lines
16 KiB
PHP
720 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Entity;
|
|
|
|
use App\Repository\UserRepository;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
|
use Symfony\Component\HttpFoundation\File\File;
|
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
|
use Symfony\Component\Security\Core\User\UserInterface;
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
use Vich\UploaderBundle\Mapping\Attribute as Vich;
|
|
|
|
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
|
#[ORM\Table(name: '`user`')]
|
|
#[UniqueEntity(fields: ['email'], message: 'Un compte existe déjà avec cet email.')]
|
|
#[Vich\Uploadable]
|
|
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|
{
|
|
#[ORM\Id]
|
|
#[ORM\GeneratedValue]
|
|
#[ORM\Column]
|
|
private ?int $id = null;
|
|
|
|
#[ORM\Column(length: 180, unique: true)]
|
|
#[Assert\Email(message: 'L\'adresse email n\'est pas valide.')]
|
|
#[Assert\NotBlank(message: 'L\'email est requis.')]
|
|
private ?string $email = null;
|
|
|
|
#[ORM\Column(length: 255)]
|
|
private ?string $firstName = null;
|
|
|
|
#[ORM\Column(length: 255)]
|
|
private ?string $lastName = null;
|
|
|
|
/** @var list<string> */
|
|
#[ORM\Column]
|
|
private array $roles = [];
|
|
|
|
#[ORM\Column]
|
|
private ?string $password = null;
|
|
|
|
#[ORM\Column(length: 255, unique: true, nullable: true)]
|
|
private ?string $keycloakId = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $companyName = null;
|
|
|
|
#[ORM\Column(length: 14, nullable: true)]
|
|
private ?string $siret = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $address = null;
|
|
|
|
#[ORM\Column(length: 10, nullable: true)]
|
|
private ?string $postalCode = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $city = null;
|
|
|
|
#[ORM\Column(length: 20, nullable: true)]
|
|
private ?string $phone = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $website = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $facebook = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $instagram = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $twitter = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $tiktok = null;
|
|
|
|
#[Vich\UploadableField(mapping: 'organizer_logo', fileNameProperty: 'logoName')]
|
|
private ?File $logoFile = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $logoName = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?\DateTimeImmutable $updatedAt = null;
|
|
|
|
#[ORM\Column(length: 6, nullable: true)]
|
|
private ?string $resetCode = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?\DateTimeImmutable $resetCodeExpiresAt = null;
|
|
|
|
#[ORM\Column]
|
|
private bool $isVerified = false;
|
|
|
|
#[ORM\Column]
|
|
private bool $isApproved = false;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?bool $isSuspended = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $offer = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?float $commissionRate = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $stripeAccountId = null;
|
|
|
|
#[ORM\Column(length: 50, nullable: true)]
|
|
private ?string $stripeStatus = null;
|
|
|
|
#[ORM\Column]
|
|
private bool $stripeChargesEnabled = false;
|
|
|
|
#[ORM\Column]
|
|
private bool $stripePayoutsEnabled = false;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?bool $isBilling = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?int $billingAmount = null;
|
|
|
|
#[ORM\Column(length: 20, nullable: true)]
|
|
private ?string $billingState = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $billingStripeSubscriptionId = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?int $debt = null;
|
|
|
|
#[ORM\ManyToOne(targetEntity: self::class)]
|
|
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
|
private ?self $parentOrganizer = null;
|
|
|
|
/** @var list<string> */
|
|
#[ORM\Column(nullable: true)]
|
|
private ?array $subAccountPermissions = null;
|
|
|
|
#[ORM\Column(length: 64, nullable: true)]
|
|
private ?string $emailVerificationToken = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?\DateTimeImmutable $emailVerifiedAt = null;
|
|
|
|
#[ORM\Column]
|
|
private \DateTimeImmutable $createdAt;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->createdAt = new \DateTimeImmutable();
|
|
}
|
|
|
|
public function getId(): ?int
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function getEmail(): ?string
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
public function setEmail(string $email): static
|
|
{
|
|
$this->email = $email;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getFirstName(): ?string
|
|
{
|
|
return $this->firstName;
|
|
}
|
|
|
|
public function setFirstName(string $firstName): static
|
|
{
|
|
$this->firstName = $firstName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getLastName(): ?string
|
|
{
|
|
return $this->lastName;
|
|
}
|
|
|
|
public function setLastName(string $lastName): static
|
|
{
|
|
$this->lastName = $lastName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getUserIdentifier(): string
|
|
{
|
|
return (string) $this->email;
|
|
}
|
|
|
|
/** @return list<string> */
|
|
public function getRoles(): array
|
|
{
|
|
$roles = $this->roles;
|
|
$roles[] = 'ROLE_USER';
|
|
|
|
return array_unique($roles);
|
|
}
|
|
|
|
/** @param list<string> $roles */
|
|
public function setRoles(array $roles): static
|
|
{
|
|
$this->roles = $roles;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPassword(): ?string
|
|
{
|
|
return $this->password;
|
|
}
|
|
|
|
public function setPassword(string $password): static
|
|
{
|
|
$this->password = $password;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getCreatedAt(): \DateTimeImmutable
|
|
{
|
|
return $this->createdAt;
|
|
}
|
|
|
|
public function getCompanyName(): ?string
|
|
{
|
|
return $this->companyName;
|
|
}
|
|
|
|
public function setCompanyName(?string $companyName): static
|
|
{
|
|
$this->companyName = $companyName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getSiret(): ?string
|
|
{
|
|
return $this->siret;
|
|
}
|
|
|
|
public function setSiret(?string $siret): static
|
|
{
|
|
$this->siret = $siret;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAddress(): ?string
|
|
{
|
|
return $this->address;
|
|
}
|
|
|
|
public function setAddress(?string $address): static
|
|
{
|
|
$this->address = $address;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPostalCode(): ?string
|
|
{
|
|
return $this->postalCode;
|
|
}
|
|
|
|
public function setPostalCode(?string $postalCode): static
|
|
{
|
|
$this->postalCode = $postalCode;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getCity(): ?string
|
|
{
|
|
return $this->city;
|
|
}
|
|
|
|
public function setCity(?string $city): static
|
|
{
|
|
$this->city = $city;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPhone(): ?string
|
|
{
|
|
return $this->phone;
|
|
}
|
|
|
|
public function setPhone(?string $phone): static
|
|
{
|
|
$this->phone = $phone;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getWebsite(): ?string
|
|
{
|
|
return $this->website;
|
|
}
|
|
|
|
public function setWebsite(?string $website): static
|
|
{
|
|
$this->website = $website;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getFacebook(): ?string
|
|
{
|
|
return $this->facebook;
|
|
}
|
|
|
|
public function setFacebook(?string $facebook): static
|
|
{
|
|
$this->facebook = $facebook;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getInstagram(): ?string
|
|
{
|
|
return $this->instagram;
|
|
}
|
|
|
|
public function setInstagram(?string $instagram): static
|
|
{
|
|
$this->instagram = $instagram;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getTwitter(): ?string
|
|
{
|
|
return $this->twitter;
|
|
}
|
|
|
|
public function setTwitter(?string $twitter): static
|
|
{
|
|
$this->twitter = $twitter;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getTiktok(): ?string
|
|
{
|
|
return $this->tiktok;
|
|
}
|
|
|
|
public function setTiktok(?string $tiktok): static
|
|
{
|
|
$this->tiktok = $tiktok;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getLogoFile(): ?File
|
|
{
|
|
return $this->logoFile;
|
|
}
|
|
|
|
public function setLogoFile(?File $logoFile = null): static
|
|
{
|
|
$this->logoFile = $logoFile;
|
|
|
|
if (null !== $logoFile) {
|
|
$this->updatedAt = new \DateTimeImmutable();
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getLogoName(): ?string
|
|
{
|
|
return $this->logoName;
|
|
}
|
|
|
|
public function setLogoName(?string $logoName): static
|
|
{
|
|
$this->logoName = $logoName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getUpdatedAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->updatedAt;
|
|
}
|
|
|
|
public function getCommissionRate(): ?float
|
|
{
|
|
return $this->commissionRate;
|
|
}
|
|
|
|
public function setCommissionRate(?float $commissionRate): static
|
|
{
|
|
$this->commissionRate = $commissionRate;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getStripeAccountId(): ?string
|
|
{
|
|
return $this->stripeAccountId;
|
|
}
|
|
|
|
public function setStripeAccountId(?string $stripeAccountId): static
|
|
{
|
|
$this->stripeAccountId = $stripeAccountId;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getStripeStatus(): ?string
|
|
{
|
|
return $this->stripeStatus;
|
|
}
|
|
|
|
public function setStripeStatus(?string $stripeStatus): static
|
|
{
|
|
$this->stripeStatus = $stripeStatus;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isStripeChargesEnabled(): bool
|
|
{
|
|
return $this->stripeChargesEnabled;
|
|
}
|
|
|
|
public function setStripeChargesEnabled(bool $stripeChargesEnabled): static
|
|
{
|
|
$this->stripeChargesEnabled = $stripeChargesEnabled;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isStripePayoutsEnabled(): bool
|
|
{
|
|
return $this->stripePayoutsEnabled;
|
|
}
|
|
|
|
public function setStripePayoutsEnabled(bool $stripePayoutsEnabled): static
|
|
{
|
|
$this->stripePayoutsEnabled = $stripePayoutsEnabled;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isBilling(): ?bool
|
|
{
|
|
return $this->isBilling;
|
|
}
|
|
|
|
public function setIsBilling(?bool $isBilling): static
|
|
{
|
|
$this->isBilling = $isBilling;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBillingAmount(): ?int
|
|
{
|
|
return $this->billingAmount;
|
|
}
|
|
|
|
public function setBillingAmount(?int $billingAmount): static
|
|
{
|
|
$this->billingAmount = $billingAmount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBillingState(): ?string
|
|
{
|
|
return $this->billingState;
|
|
}
|
|
|
|
public function setBillingState(?string $billingState): static
|
|
{
|
|
$this->billingState = $billingState;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBillingStripeSubscriptionId(): ?string
|
|
{
|
|
return $this->billingStripeSubscriptionId;
|
|
}
|
|
|
|
public function setBillingStripeSubscriptionId(?string $billingStripeSubscriptionId): static
|
|
{
|
|
$this->billingStripeSubscriptionId = $billingStripeSubscriptionId;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getDebt(): ?int
|
|
{
|
|
return $this->debt;
|
|
}
|
|
|
|
public function setDebt(?int $debt): static
|
|
{
|
|
$this->debt = $debt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addDebt(int $amount): static
|
|
{
|
|
$this->debt = ($this->debt ?? 0) + $amount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function reduceDebt(int $amount): static
|
|
{
|
|
$this->debt = max(0, ($this->debt ?? 0) - $amount);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getParentOrganizer(): ?self
|
|
{
|
|
return $this->parentOrganizer;
|
|
}
|
|
|
|
public function setParentOrganizer(?self $parentOrganizer): static
|
|
{
|
|
$this->parentOrganizer = $parentOrganizer;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return list<string>|null
|
|
*/
|
|
public function getSubAccountPermissions(): ?array
|
|
{
|
|
return $this->subAccountPermissions;
|
|
}
|
|
|
|
/**
|
|
* @param list<string>|null $subAccountPermissions
|
|
*/
|
|
public function setSubAccountPermissions(?array $subAccountPermissions): static
|
|
{
|
|
$this->subAccountPermissions = $subAccountPermissions;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function hasPermission(string $permission): bool
|
|
{
|
|
return null !== $this->subAccountPermissions && \in_array($permission, $this->subAccountPermissions, true);
|
|
}
|
|
|
|
public function getResetCode(): ?string
|
|
{
|
|
return $this->resetCode;
|
|
}
|
|
|
|
public function setResetCode(?string $resetCode): static
|
|
{
|
|
$this->resetCode = $resetCode;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getResetCodeExpiresAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->resetCodeExpiresAt;
|
|
}
|
|
|
|
public function setResetCodeExpiresAt(?\DateTimeImmutable $resetCodeExpiresAt): static
|
|
{
|
|
$this->resetCodeExpiresAt = $resetCodeExpiresAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isApproved(): bool
|
|
{
|
|
return $this->isApproved;
|
|
}
|
|
|
|
public function setIsApproved(bool $isApproved): static
|
|
{
|
|
$this->isApproved = $isApproved;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isSuspended(): ?bool
|
|
{
|
|
return $this->isSuspended;
|
|
}
|
|
|
|
public function setIsSuspended(?bool $isSuspended): static
|
|
{
|
|
$this->isSuspended = $isSuspended;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getOffer(): ?string
|
|
{
|
|
return $this->offer;
|
|
}
|
|
|
|
public function setOffer(?string $offer): static
|
|
{
|
|
$this->offer = $offer;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isVerified(): bool
|
|
{
|
|
return $this->isVerified;
|
|
}
|
|
|
|
public function setIsVerified(bool $isVerified): static
|
|
{
|
|
$this->isVerified = $isVerified;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getEmailVerificationToken(): ?string
|
|
{
|
|
return $this->emailVerificationToken;
|
|
}
|
|
|
|
public function setEmailVerificationToken(?string $emailVerificationToken): static
|
|
{
|
|
$this->emailVerificationToken = $emailVerificationToken;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getEmailVerifiedAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->emailVerifiedAt;
|
|
}
|
|
|
|
public function setEmailVerifiedAt(?\DateTimeImmutable $emailVerifiedAt): static
|
|
{
|
|
$this->emailVerifiedAt = $emailVerifiedAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getKeycloakId(): ?string
|
|
{
|
|
return $this->keycloakId;
|
|
}
|
|
|
|
public function setKeycloakId(?string $keycloakId): static
|
|
{
|
|
$this->keycloakId = $keycloakId;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function eraseCredentials(): void
|
|
{
|
|
// Required by UserInterface — no temporary credentials to clear
|
|
}
|
|
|
|
public function getSlug(): string
|
|
{
|
|
$name = $this->companyName ?? $this->firstName.' '.$this->lastName;
|
|
$slug = mb_strtolower(trim($name));
|
|
$slug = transliterator_transliterate('Any-Latin; Latin-ASCII', $slug) ?: $slug;
|
|
$slug = (string) preg_replace('/[^a-z0-9]+/', '-', $slug);
|
|
$slug = trim($slug, '-');
|
|
|
|
return '' === $slug ? 'organisateur' : $slug;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function __serialize(): array
|
|
{
|
|
return [
|
|
'id' => $this->id,
|
|
'email' => $this->email,
|
|
'password' => $this->password,
|
|
'roles' => $this->roles,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public function __unserialize(array $data): void
|
|
{
|
|
$this->id = $data['id'] ?? null;
|
|
$this->email = $data['email'] ?? null;
|
|
$this->password = $data['password'] ?? null;
|
|
$this->roles = $data['roles'] ?? [];
|
|
}
|
|
}
|