<?php declare(strict_types=1);
namespace Sq\Entity\Schema\ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use ReflectionClass;
use Sq\Entity\Schema\Column\OrganizationBillingCycle;
use Sq\Entity\Schema\Column\OrganizationSubscriptionStatus;
use Sq\Entity\Schema\Column\OrganizationSuspended;
use Sq\Service\Repository\ORM\OrganizationRepository;
use Symfony\Component\Uid\Uuid;
#[ORM\Entity(repositoryClass: OrganizationRepository::class)]
#[ORM\Table(name: 'organizations')]
class Organization
{
/**
*
* @var int|null
*/
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(name: 'o_id', type: 'integer', nullable: false, options: ['unsigned' => true])]
private $id;
/**
*
* @var Member
*/
#[ORM\OneToOne(targetEntity: Member::class, inversedBy: 'organization', fetch: 'EAGER', cascade: ['persist'])]
#[ORM\JoinColumn(name: 'o_m_id', referencedColumnName: 'm_id', nullable: false)]
private $legacyMember;
/**
* @var string
*/
#[ORM\Column(name: 'o_name', type: 'string', nullable: false)]
private $name;
/**
* @var Collection
*/
#[ORM\OneToMany(targetEntity: Workspace::class, mappedBy: 'organization', fetch: 'LAZY', cascade: ['persist'])]
private $workspaces;
/**
* @var OrganizationSettings
*/
#[ORM\OneToOne(targetEntity: OrganizationSettings::class, mappedBy: 'organization', cascade: ['persist'])]
private $settings;
/**
* @var Collection
*/
#[ORM\OneToMany(targetEntity: UserOrganizationAssignment::class, mappedBy: 'organization', fetch: 'LAZY', orphanRemoval: true)]
private $userAssignments;
/**
* @var Collection
*/
#[ORM\OneToMany(targetEntity: UserInvitation::class, mappedBy: 'organization', fetch: 'LAZY')]
private $userInvitations;
/**
* @var Collection<Bitly>
*/
#[ORM\OneToMany(targetEntity: Bitly::class, mappedBy: 'organization', fetch: 'LAZY')]
private $bitlyAccounts;
/**
* @var string|null
*/
#[ORM\Column(name: 'o_sub_status', type: 'string', nullable: false, options: ['default' => 'trial'])]
private $subscriptionStatus = OrganizationSubscriptionStatus::TRIAL;
/**
* @var string|null
*/
#[ORM\Column(name: 'o_subscription_extid', type: 'integer', nullable: true)]
private $subscriptionExternalId;
/**
* @var BillingDetails|null
*/
#[ORM\OneToOne(targetEntity: BillingDetails::class, mappedBy: 'organization')]
private $billingDetails;
/**
* @var string
*/
#[ORM\Column(name: 'o_billing_cycle', type: 'string', nullable: false, options: ['default' => 'monthly'])]
private $billingCycle = OrganizationBillingCycle::MONTHLY;
/**
* @var \DateTimeInterface|null
*/
#[ORM\Column(name: 'o_trial_expiry_local_date', type: 'date', nullable: true)]
private $trialExpiryLocalDate;
/**
* @var \DateTimeInterface|null
*/
#[ORM\Column(name: 'o_next_billing_date', type: 'date', nullable: true)]
private $nextBillingDate;
/**
* @var string|null
*/
#[ORM\Column(name: 'o_next_bill_amount', type: 'decimal', precision: 8, scale: 2, nullable: true, options: ['default' => '0.00'])]
private $nextBillAmount = '0.00';
/**
* @var string|null
*/
#[ORM\Column(name: 'o_balance', type: 'decimal', precision: 15, scale: 2, nullable: true, options: ['default' => '0.00'])]
private $balance = '0.00';
/**
* @var string|null
*/
#[ORM\Column(name: 'o_currency', type: 'string', length: 3, nullable: true)]
private $currency;
/**
* @var \DateTimeInterface|null
*/
#[ORM\Column(name: 'o_date_activated', type: 'datetime', nullable: true)]
private $dateActivated;
/**
* @var \DateTimeInterface|null
*/
#[ORM\Column(name: 'o_date_upgraded', type: 'datetime', nullable: true)]
private $dateUpgraded;
/**
* @var string|null
*/
#[ORM\Column(name: 'o_country_code', type: 'string', length: 2, nullable: true)]
private $countryCode;
/**
* @var int
*/
#[ORM\Column(name: 'o_plan_id', type: 'smallint', nullable: false, options: ['default' => 1, 'unsigned' => true])]
private $planId = 1;
/**
* @var int|null
*/
#[ORM\Column(name: 'o_plan_group', type: 'smallint', nullable: true, options: ['default' => 3, 'unsigned' => true])]
private $planGroup = 4;
/**
* @var int|null
*/
#[ORM\Column(name: 'o_addon_group', type: 'smallint', nullable: true, options: ['default' => 1, 'unsigned' => true])]
private $addonGroup = 2;
/**
* @var bool|null
*/
#[ORM\Column(name: 'o_custom_plans', type: 'boolean', nullable: true, options: ['default' => 1])]
private $customPlans = true;
/**
* @var int|null
*/
#[ORM\Column(name: 'o_onboarding', type: 'smallint', nullable: true, options: ['default' => 1])]
private $onboarding = 1;
/**
* @var bool|null
*/
#[ORM\Column(name: 'o_early_upgrade_offer', type: 'boolean', nullable: true, options: ['default' => false])]
private $earlyUpgradeOffer = false;
/**
* @var int|null
*/
#[ORM\Column(name: 'o_pricing_design', type: 'integer', nullable: true, options: ['default' => 1])]
private $pricingDesign = 2;
/**
* @var string|null
*/
#[ORM\Column(name: 'o_ref', type: 'string', length: 45, nullable: true)]
private $ref;
/**
* @var bool|null
*/
#[ORM\Column(name: 'o_ref_gift', type: 'boolean', nullable: true, options: ['default' => false])]
private $refGift = false;
/**
* @var int|null
*/
#[ORM\Column(name: 'o_suspended', type: 'integer', nullable: true, options: ['default' => 0])]
private $suspended = OrganizationSuspended::ACTIVE;
public function __construct(
string $name,
string $email,
string $timezone,
?string $countryCode,
string $billingCycle = 'monthly',
int $trialLength = 14,
?\DateTimeInterface $firstVisit = null,
) {
$name = trim($name);
$this->name = $name;
$this->legacyMember = new Member(
username: Uuid::v4()->toRfc4122(), // Not used for anything for overhaul, but cant be null.
email: $email,
firstName: $name,
timezone: $timezone,
countryCode: $countryCode,
billingCycle: $billingCycle,
trialLength: $trialLength,
organization: $this,
firstVisit: $firstVisit
);
$this->legacyMember->skipOnboarding();
$this->legacyMember->setInOverhaul(true);
$this->createDefaultsForNewInstance();
}
private function createDefaultsForNewInstance(): self
{
$this->settings = new OrganizationSettings($this);
$this->userAssignments = new ArrayCollection;
$this->userInvitations = new ArrayCollection;
$this->workspaces = new ArrayCollection;
$this->bitlyAccounts = new ArrayCollection;
return $this;
}
/**
* @throws \ReflectionException
*/
public static function createForExistingLegacyMember(Member $member): Organization
{
$org = (new ReflectionClass(static::class))
->newInstanceWithoutConstructor()
->createDefaultsForNewInstance();
$org->name = $member->getBusiness()?->getCompany() ?? "Default Organization for " . $member->getFirstName();
$org->legacyMember = $member;
return $org;
}
public function getId(): ?int
{
return $this->id;
}
/**
* Every org has a member entity for billing/subscription purposes.
*/
public function getLegacyMember(): Member
{
return $this->legacyMember;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = trim($name);
return $this;
}
public function getSettings(): OrganizationSettings
{
return $this->settings;
}
/**
* @return Collection|Workspace[]
*/
public function getWorkspaces(bool $includeDeleted = false): Collection
{
if ($includeDeleted)
{
return $this->workspaces;
}
return $this->workspaces->filter(
fn (Workspace $workspace): bool => !$workspace->isDeleted()
);
}
public function addWorkspace(Workspace $workspace): self
{
if (!$this->workspaces->contains($workspace))
{
$this->workspaces->add($workspace);
}
return $this;
}
public function removeWorkspace(Workspace $workspace): self
{
if ($this->workspaces->contains($workspace))
{
$this->workspaces->removeElement($workspace);
}
return $this;
}
/**
* @return Collection|User[]
*/
public function getUsers(): Collection
{
return $this->userAssignments->map(function (UserOrganizationAssignment $assignment): User
{
return $assignment->getUser();
});
}
public function getOwner(): User
{
return $this->userAssignments
->filter(fn (UserOrganizationAssignment $assignment) => $assignment->getRole() === UserOrganizationAssignment::ROLE_OWNER)
->first()
->getUser();
}
public function hasUser(User $user): bool
{
return $this->getUsers()->contains($user);
}
/**
* @return Collection<UserOrganizationAssignment>
*/
public function getUserAssignments(): Collection
{
return $this->userAssignments;
}
public function getUserAssignment(User $user): ?UserOrganizationAssignment
{
$assignments = $this->userAssignments->filter(function (UserOrganizationAssignment $assignment) use ($user)
{
return $assignment->getUser() === $user;
});
return $assignments->count() > 0 ? $assignments->first() : null;
}
public function getUserInvitations(): Collection
{
return $this->userInvitations;
}
public function addUser(User $user, string $role): self
{
$user->addToOrganization($this, $role);
return $this;
}
public function removeUser(User $user): self
{
$user->removeFromOrganization($this);
return $this;
}
public function getSubscriptionStatus(): ?string
{
return $this->subscriptionStatus;
}
public function setSubscriptionStatus(?string $subscriptionStatus): self
{
$this->subscriptionStatus = $subscriptionStatus;
return $this;
}
public function getSubscriptionExternalId(): ?string
{
return $this->subscriptionExternalId;
}
public function setSubscriptionExternalId(?string $subscriptionExternalId): self
{
$this->subscriptionExternalId = $subscriptionExternalId;
return $this;
}
public function getBillingDetails(): ?BillingDetails
{
return $this->billingDetails;
}
public function getBillingCycle(): string
{
return $this->billingCycle;
}
public function setBillingCycle(string $billingCycle): self
{
$this->billingCycle = $billingCycle;
return $this;
}
public function getTrialExpiryLocalDate(): ?\DateTimeInterface
{
return $this->trialExpiryLocalDate !== null
? clone $this->trialExpiryLocalDate
: null;
}
public function setTrialExpiryLocalDate(?\DateTimeInterface $trialExpiryLocalDate): self
{
$this->trialExpiryLocalDate = $trialExpiryLocalDate !== null ? clone $trialExpiryLocalDate : null;
return $this;
}
public function getNextBillingDate(): ?\DateTimeInterface
{
return $this->nextBillingDate !== null
? clone $this->nextBillingDate
: null;
}
public function setNextBillingDate(?\DateTimeInterface $nextBillingDate): self
{
$this->nextBillingDate = $nextBillingDate !== null ? clone $nextBillingDate : null;
return $this;
}
public function getNextBillAmount(): ?string
{
return $this->nextBillAmount;
}
public function setNextBillAmount(?string $nextBillAmount): self
{
$this->nextBillAmount = $nextBillAmount;
return $this;
}
public function getBalance(): ?string
{
return $this->balance;
}
public function setBalance(?string $balance): self
{
$this->balance = $balance;
return $this;
}
public function getCurrency(): ?string
{
return $this->currency;
}
public function setCurrency(?string $currency): self
{
$this->currency = $currency;
return $this;
}
public function getDateActivated(): ?\DateTimeInterface
{
return $this->dateActivated !== null
? clone $this->dateActivated
: null;
}
public function setDateActivated(?\DateTimeInterface $dateActivated): self
{
$this->dateActivated = $dateActivated !== null ? clone $dateActivated : null;
return $this;
}
public function getDateUpgraded(): ?\DateTimeInterface
{
return $this->dateUpgraded !== null
? clone $this->dateUpgraded
: null;
}
public function setDateUpgraded(?\DateTimeInterface $dateUpgraded): self
{
$this->dateUpgraded = $dateUpgraded !== null ? clone $dateUpgraded : null;
return $this;
}
public function getCountryCode(): ?string
{
return $this->countryCode;
}
public function setCountryCode(?string $countryCode): self
{
$this->countryCode = $countryCode;
return $this;
}
public function getAddonGroup(): ?int
{
return $this->addonGroup;
}
public function setAddonGroup(?int $addonGroup): self
{
$this->addonGroup = $addonGroup;
return $this;
}
public function hasCustomPlans(): ?bool
{
return $this->customPlans;
}
public function setCustomPlans(?bool $customPlans): self
{
$this->customPlans = $customPlans;
return $this;
}
public function getOnboardingVersion(): int
{
return $this->onboarding ?? 1; // TODO Default to the minimum available onboarding version.
}
public function setOnboardingVersion(int $onboarding): self
{
$this->onboarding = $onboarding;
return $this;
}
public function isEarlyUpgradeOffer(): ?bool
{
return $this->earlyUpgradeOffer;
}
public function setEarlyUpgradeOffer(?bool $earlyUpgradeOffer): self
{
$this->earlyUpgradeOffer = $earlyUpgradeOffer;
return $this;
}
public function getPricingDesign(): ?int
{
return $this->pricingDesign;
}
public function setPricingDesign(?int $pricingDesign): self
{
$this->pricingDesign = $pricingDesign;
return $this;
}
public function getRef(): ?string
{
return $this->ref;
}
public function setRef(?string $ref): self
{
$this->ref = $ref;
return $this;
}
public function isRefGift(): ?bool
{
return $this->refGift;
}
public function setRefGift(?bool $refGift): self
{
$this->refGift = $refGift;
return $this;
}
public function isSuspended(): ?int
{
return $this->suspended;
}
public function setSuspended(?int $suspended): self
{
$this->suspended = $suspended;
return $this;
}
public function getPlan(): Plan
{
return $this->getLegacyMember()->getPlan();
}
public function changePlan(Plan $plan): self
{
$this->getLegacyMember()->changePlan($plan);
return $this;
}
/**
* @return Collection|Bitly[]
*/
public function getBitlyAccounts(): Collection
{
return $this->bitlyAccounts;
}
}