<?php
namespace App\Entity;
use App\Entity\Traits\ActiveTrait;
use App\Entity\Traits\AddressTrait;
use App\Entity\Traits\ErrorTrait;
use App\Entity\Traits\OnbeTrait;
use App\Entity\Traits\W9Trait;
use App\Entity\Traits\W8Trait;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass=UserRepository::class)
* @ORM\Table(name="`user`")
* @UniqueEntity(fields={"email"}, message="This email is already registered in Jewelry Rewards. Please click SIGN IN in the top navigation to sign in or reset your password.")
*/
class User implements UserInterface, PasswordAuthenticatedUserInterface, TwoFactorInterface
{
use ActiveTrait;
use AddressTrait;
use W9Trait;
use W8Trait;
use OnbeTrait;
use ErrorTrait;
use TimestampableEntity;
public const ROLE_DEFAULT = 'ROLE_USER';
public const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
public const ROLE_ADMIN = 'ROLE_ADMIN';
public const ROLE_CORP_ADMIN = 'ROLE_CORP_ADMIN';
public const ROLE_MK_CORP_ADMIN = 'ROLE_MK_CORP_ADMIN';
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
private $password;
public $plainPassword;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $firstName;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $lastName;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Assert\Regex(pattern="/(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}/", message="Phone number is not valid")
*/
private $phone;
/**
* @ORM\OneToMany(targetEntity=Submission::class, mappedBy="user", orphanRemoval=true)
*/
private $submissions;
private $newEmail = null;
private $termsAccepted = false;
public $invitationCode;
public $retailer;
public $retailerName;
public $brand;
/**
* @ORM\Column(type="string", length=500, nullable=true)
*/
private $ssn;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $workEmail;
/**
* @ORM\OneToMany(targetEntity=UserInvitationCode::class, mappedBy="user", orphanRemoval=true, cascade={"persist"})
*/
private $userInvitationCodes;
/**
* @ORM\OneToMany(targetEntity=EducationQuizResult::class, mappedBy="user")
*/
private $educationQuizResults;
/**
* @ORM\OneToMany(targetEntity=EducationUserHistory::class, mappedBy="user")
*/
private $educationUserHistories;
/**
* @ORM\OneToMany(targetEntity=Reward::class, mappedBy="user", orphanRemoval=true)
*/
private $rewards;
/**
* @ORM\Column(type="boolean")
*/
private $optOut = false;
/**
* @ORM\OneToMany(targetEntity=OnbeTransaction::class, mappedBy="user", orphanRemoval=true)
*/
private $onbeTransactions;
private $loggedUser = null;
/**
* @ORM\OneToMany(targetEntity=UserRetailer::class, mappedBy="user", orphanRemoval=true, cascade={"all"})
*/
private $userRetailers;
/**
* @ORM\OneToMany(targetEntity=ImportFile::class, mappedBy="user")
*/
private $importFiles;
/**
* @ORM\Column(type="string", length=500, nullable=true)
*/
private $authCode;
/**
* @ORM\Column(type="boolean")
*/
private $completed = true;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $external_id;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $store_id;
/**
* @ORM\OneToMany(targetEntity=OnbeAccount::class, mappedBy="user", cascade={"all"})
*/
private $onbeAccounts;
public function __construct()
{
$this->submissions = new ArrayCollection();
$this->userInvitationCodes = new ArrayCollection();
$this->educationQuizResults = new ArrayCollection();
$this->educationUserHistories = new ArrayCollection();
$this->rewards = new ArrayCollection();
$this->onbeTransactions = new ArrayCollection();
$this->userRetailers = new ArrayCollection();
$this->importFiles = new ArrayCollection();
$this->onbeAccounts = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* @deprecated since Symfony 5.3, use getUserIdentifier instead
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
if (in_array(strtoupper(self::ROLE_CORP_ADMIN), $roles, true)){
foreach ($this->getUserBrands() as $brand){
if ($brand->getName() == 'MK Diamonds'){
$roles[] = self::ROLE_MK_CORP_ADMIN;
break;
}
}
}
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* {@inheritdoc}
*/
public function hasRole($role): bool
{
return in_array(strtoupper($role), $this->getRoles(), true);
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function addRole($role): self
{
$roles = $this->roles;
$roles[] = $role;
$this->setRoles(array_unique($roles));
return $this;
}
public static function getRolesList(): array
{
return [
self::ROLE_DEFAULT => 'User',
self::ROLE_SUPER_ADMIN => 'Super Admin',
self::ROLE_ADMIN => 'Admin',
self::ROLE_CORP_ADMIN => 'Corporate Admin',
self::ROLE_MK_CORP_ADMIN => 'MK Corporate Admin',
];
}
public function getRolesLabels(): string
{
$rolesList = self::getRolesList();
$roles = [];
foreach ($this->getRoles() as $role){
if($role !== self::ROLE_DEFAULT){
$roles[] = "<span class='label label-info'>{$rolesList[$role]}</span> ";
}
}
return implode(' ', $roles);
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* @see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getFirstName(): ?string
{
return $this->firstName;
}
public function setFirstName(?string $firstName): self
{
$this->firstName = $firstName;
return $this;
}
public function getLastName(): ?string
{
return $this->lastName;
}
public function setLastName(?string $lastName): self
{
$this->lastName = $lastName;
return $this;
}
public function getPhone(): ?string
{
return $this->phone;
}
public function setPhone(?string $phone): self
{
$this->phone = $phone;
return $this;
}
/**
* @return string|null
*/
public function getFullName(): string
{
return sprintf('%s %s',$this->getFirstName(), $this->getLastName());
}
/**
* @return Collection<int, Submission>
*/
public function getSubmissions(): Collection
{
return $this->submissions;
}
public function addSubmission(Submission $submission): self
{
if (!$this->submissions->contains($submission)) {
$this->submissions[] = $submission;
$submission->setUser($this);
}
return $this;
}
public function removeSubmission(Submission $submission): self
{
if ($this->submissions->removeElement($submission)) {
// set the owning side to null (unless already changed)
if ($submission->getUser() === $this) {
$submission->setUser(null);
}
}
return $this;
}
public function getSsn(): ?string
{
return $this->ssn;
}
public function setSsn(?string $ssn): self
{
$this->ssn = $ssn;
return $this;
}
/**
* @return null
*/
public function getNewEmail()
{
return $this->newEmail;
}
/**
* @param null $newEmail
*/
public function setNewEmail($newEmail): void
{
$this->newEmail = $newEmail;
}
/**
* @return bool
*/
public function isTermsAccepted(): bool
{
return $this->termsAccepted;
}
/**
* @param bool $termsAccepted
*/
public function setTermsAccepted(bool $termsAccepted): void
{
$this->termsAccepted = $termsAccepted;
}
public function getWorkEmail(): ?string
{
return $this->workEmail;
}
public function setWorkEmail(?string $workEmail): self
{
$this->workEmail = $workEmail;
return $this;
}
/**
* @return Collection<int, UserInvitationCode>
*/
public function getUserInvitationCodes(User $user = null): Collection
{
/**
* I need it because I cannot override sonata admin's export logic
*
*/
$user = $this->getLoggedUser();
if ($user && $user->hasRole(self::ROLE_CORP_ADMIN)){
$userBrands = $user->getUserBrands();
return $this->userInvitationCodes->filter(function (UserInvitationCode $userInvitationCode) use ($userBrands){
return isset($userBrands[$userInvitationCode->getInvitationCode()->getBrand()->getId()]);
});
}
return $this->userInvitationCodes;
}
public function addUserInvitationCode(UserInvitationCode $userInvitationCode): self
{
if (!$this->userInvitationCodes->contains($userInvitationCode)) {
$this->userInvitationCodes[] = $userInvitationCode;
$userInvitationCode->setUser($this);
}
return $this;
}
public function setUserInvitationCodes($userInvitationCodes): self
{
$this->userInvitationCodes = $userInvitationCodes;
return $this;
}
public function removeUserInvitationCode(UserInvitationCode $userInvitationCode): self
{
if ($this->userInvitationCodes->removeElement($userInvitationCode)) {
// set the owning side to null (unless already changed)
if ($userInvitationCode->getUser() === $this) {
$userInvitationCode->setUser(null);
}
}
return $this;
}
public function __toString()
{
return (string)$this->getFullName();
}
/**
* @param User|null $user
* @return array
*/
public function getUserBrands(User $user = null): array
{
$brands = [];
foreach ($this->getUserRetailers() as $userRetailer){
if ($userRetailer->isApproved()){
// First check if UserRetailer has a direct brand association
if ($userRetailer->getBrand()) {
$brand = $userRetailer->getBrand();
$brands[$brand->getId()] = $brand;
}
// Otherwise, check store brands
/*elseif ($userRetailer->getStore() && $userRetailer->getStore()->getActive()) {
foreach ($userRetailer->getStore()->getBrands() as $brand) {
$brands[$brand->getId()] = $brand;
}
}
// Fallback to legacy retailer brand
elseif ($userRetailer->getRetailer() && $userRetailer->getRetailer()->getActive()) {
$brand = $userRetailer->getRetailer()->getBrand();
if ($brand) {
$brands[$brand->getId()] = $brand;
}
}*/
}
}
return $brands;
}
public function getDefaultBrand(): ?Brand
{
if (count($this->getUserInvitationCodes()) === 1){
return $this->getUserInvitationCodes()->first()->getInvitationCode()->getBrand();
}
return null;
}
/**
* @param User|null $user
* @return array
*/
public function getUserRetailersOld(User $user = null): array
{
$retailers = [];
foreach ($this->getUserInvitationCodes($user) as $userInvitationCode){
$retailer = $userInvitationCode->getInvitationCode()->getRetailer();
$retailers[$retailer->getId()] = $retailer;
}
return $retailers;
}
/**
* @return Collection<int, EducationQuizResult>
*/
public function getEducationQuizResults(): Collection
{
return $this->educationQuizResults;
}
public function addEducationQuizResult(EducationQuizResult $educationQuizResult): self
{
if (!$this->educationQuizResults->contains($educationQuizResult)) {
$this->educationQuizResults[] = $educationQuizResult;
$educationQuizResult->setUser($this);
}
return $this;
}
public function removeEducationQuizResult(EducationQuizResult $educationQuizResult): self
{
if ($this->educationQuizResults->removeElement($educationQuizResult)) {
// set the owning side to null (unless already changed)
if ($educationQuizResult->getUser() === $this) {
$educationQuizResult->setUser(null);
}
}
return $this;
}
/**
* @return Collection<int, EducationUserHistory>
*/
public function getEducationUserHistories(): Collection
{
return $this->educationUserHistories;
}
public function addEducationUserHistory(EducationUserHistory $educationUserHistory): self
{
if (!$this->educationUserHistories->contains($educationUserHistory)) {
$this->educationUserHistories[] = $educationUserHistory;
$educationUserHistory->setUser($this);
}
return $this;
}
public function removeEducationUserHistory(EducationUserHistory $educationUserHistory): self
{
if ($this->educationUserHistories->removeElement($educationUserHistory)) {
// set the owning side to null (unless already changed)
if ($educationUserHistory->getUser() === $this) {
$educationUserHistory->setUser(null);
}
}
return $this;
}
/**
* @return Collection<int, Reward>
*/
public function getRewards(): Collection
{
return $this->rewards;
}
public function addReward(Reward $reward): self
{
if (!$this->rewards->contains($reward)) {
$this->rewards[] = $reward;
$reward->setUser($this);
}
return $this;
}
public function removeReward(Reward $reward): self
{
if ($this->rewards->removeElement($reward)) {
// set the owning side to null (unless already changed)
if ($reward->getUser() === $this) {
$reward->setUser(null);
}
}
return $this;
}
/**
* @return bool
*/
public function isOptOut(): ?bool
{
return $this->optOut;
}
/**
* @param bool $optOut
* @return User
*/
public function setOptOut(bool $optOut): self
{
$this->optOut = $optOut;
return $this;
}
public function getPaidRewardsAmount()
{
$paidRewards = 0;
foreach ($this->getRewards() as $reward){
if($reward->getStatus() == Reward::STATUS_PAID){
$paidRewards += $reward->getAmount();
}
}
return $paidRewards;
}
public function getTotalRewardsAmount()
{
$totalRewards = 0;
foreach ($this->getRewards() as $reward){
$totalRewards += $reward->getAmount();
}
return $totalRewards;
}
public function hasRetailerProducts($brand = null): bool
{
foreach ($this->getUserRetailers() as $userRetailer){
if($brand && $userRetailer->getBrand()->getId() !== $brand->getId()){
continue;
}
if (count($userRetailer->getStore()->getRetailer()->getProducts()) > 0){
return true;
}
}
return false;
}
/**
* @return Collection<int, OnbeTransaction>
*/
public function getOnbeTransactions(): Collection
{
return $this->onbeTransactions;
}
public function addOnbeTransaction(OnbeTransaction $onbeTransaction): self
{
if (!$this->onbeTransactions->contains($onbeTransaction)) {
$this->onbeTransactions[] = $onbeTransaction;
$onbeTransaction->setUser($this);
}
return $this;
}
public function removeOnbeTransaction(OnbeTransaction $onbeTransaction): self
{
if ($this->onbeTransactions->removeElement($onbeTransaction)) {
// set the owning side to null (unless already changed)
if ($onbeTransaction->getUser() === $this) {
$onbeTransaction->setUser(null);
}
}
return $this;
}
public function setLoggedUser(UserInterface $user)
{
$this->loggedUser = $user;
}
public function getLoggedUser()
{
return $this->loggedUser;
}
/**
* @return Collection<int, UserRetailer>
*/
public function getUserRetailers(): Collection
{
return $this->userRetailers;
}
public function addUserRetailer(UserRetailer $userRetailer): self
{
if (!$this->userRetailers->contains($userRetailer)) {
$this->userRetailers[] = $userRetailer;
$userRetailer->setUser($this);
}
return $this;
}
public function removeUserRetailer(UserRetailer $userRetailer): self
{
if ($this->userRetailers->removeElement($userRetailer)) {
// set the owning side to null (unless already changed)
if ($userRetailer->getUser() === $this) {
$userRetailer->setUser(null);
}
}
return $this;
}
public function hasBrandAccess(Brand $brand): bool
{
return isset($this->getUserBrands()[$brand->getId()]);
}
/**
* @return mixed
*/
public function getUnMaskedSsn()
{
return $this->unMaskedSsn;
}
/**
* @return Reward
*/
public function getTotalRewards()
{
if($this->getRewards() === null){
return 0;
}
$criteria = Criteria::create()
->andWhere(Criteria::expr()->eq('status', Reward::STATUS_PAID))
->andWhere(Criteria::expr()->gte('paidAt', new \DateTime('2023-01-01')))
;
$rewards = $this->getRewards()->matching($criteria);
$total = 0;
foreach ($rewards as $reward){
$total += $reward->getAmount();
}
return $total;
}
/**
* @return Collection<int, ImportFile>
*/
public function getImportFiles(): Collection
{
return $this->importFiles;
}
public function addImportFile(ImportFile $importFile): self
{
if (!$this->importFiles->contains($importFile)) {
$this->importFiles[] = $importFile;
$importFile->setUser($this);
}
return $this;
}
public function removeImportFile(ImportFile $importFile): self
{
if ($this->importFiles->removeElement($importFile)) {
// set the owning side to null (unless already changed)
if ($importFile->getUser() === $this) {
$importFile->setUser(null);
}
}
return $this;
}
public function isAdmin(): bool
{
return $this->hasRole(self::ROLE_SUPER_ADMIN) || $this->hasRole(self::ROLE_ADMIN) || $this->hasRole(self::ROLE_CORP_ADMIN) || $this->hasRole(self::ROLE_MK_CORP_ADMIN);
}
public function isEmailAuthEnabled(): bool
{
return false;
return $this->isAdmin();
}
public function getEmailAuthRecipient(): string
{
return $this->email;
}
public function getEmailAuthCode(): string
{
if (null === $this->authCode) {
throw new \LogicException('The authentication code was not set');
}
return $this->authCode;
}
public function setEmailAuthCode(string $authCode): void
{
$this->authCode = $authCode;
}
public function getCompleted(): ?bool
{
return $this->completed;
}
public function setCompleted(bool $completed): self
{
$this->completed = $completed;
return $this;
}
public function getExternalId(): ?int
{
return $this->external_id;
}
public function setExternalId(?int $external_id): self
{
$this->external_id = $external_id;
return $this;
}
public function getStoreId(): ?string
{
return $this->store_id;
}
public function setStoreId(?string $store_id): self
{
$this->store_id = $store_id;
return $this;
}
/**
* @return Collection<int, OnbeAccount>
*/
public function getOnbeAccounts(): Collection
{
return $this->onbeAccounts;
}
public function addOnbeAccount(OnbeAccount $onbeAccount): self
{
if (!$this->onbeAccounts->contains($onbeAccount)) {
$this->onbeAccounts[] = $onbeAccount;
$onbeAccount->setUser($this);
}
return $this;
}
public function removeOnbeAccount(OnbeAccount $onbeAccount): self
{
if ($this->onbeAccounts->removeElement($onbeAccount)) {
// set the owning side to null (unless already changed)
if ($onbeAccount->getUser() === $this) {
$onbeAccount->setUser(null);
}
}
return $this;
}
}