Files
kupshop/bundles/KupShop/PreordersBundle/Entity/Preorder.php
2025-08-02 16:30:27 +02:00

241 lines
7.0 KiB
PHP

<?php
declare(strict_types=1);
namespace KupShop\PreordersBundle\Entity;
use Doctrine\DBAL\Driver\Exception as DBALDriverException;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\ParameterType;
use KupShop\PreordersBundle\Exception\NotFoundException;
use KupShop\PreordersBundle\Exception\UnsupportedOperationException;
use KupShop\PreordersBundle\Service\PreorderUtil;
use Query\Operator as Op;
class Preorder implements \ArrayAccess
{
private array $preorder;
private ?array $dates = [];
public function __construct(
public readonly int $id,
) {
$this->fetchData();
}
public function getItemsByUser(\User|int $user, ?array $dateIds = null): ?array
{
if ($user instanceof \User) {
$user = $user->id;
}
$items = sqlQueryBuilder()
->select('pi.*')
->from('preorders_items', 'pi')
->leftJoin('pi', 'preorders_dates', 'pd', 'pi.id_preorder_date = pd.id')
->andWhere(Op::equals([
'pi.id_user' => $user,
'pd.id_preorder' => $this->id,
]));
if ($dateIds !== null) {
$items->andWhere(Op::inIntArray($dateIds, 'pd.id'));
}
$items = $items->execute()->fetchAllAssociative();
$resultItems = [];
foreach ($items as $item) {
$productId = (int) $item['id_product'];
$variationId = $item['id_variation'] ? (int) $item['id_variation'] : null;
$dateId = (int) $item['id_preorder_date'];
$itemKey = $variationId === null ? $productId : "{$productId}_{$variationId}";
if (!isset($resultItems[$itemKey])) {
$resultItems[$itemKey] = $item;
$resultItems[$itemKey]['pieces'] = 0;
$resultItems[$itemKey]['pieces_sent'] = 0;
$resultItems[$itemKey]['pieces_per_date'] = [];
$resultItems[$itemKey]['pieces_sent_per_date'] = [];
}
$resultItems[$itemKey]['pieces_per_date'][$dateId] = (int) $item['pieces'];
$resultItems[$itemKey]['pieces_sent_per_date'][$dateId] = (int) $item['pieces_sent'];
$resultItems[$itemKey]['pieces'] += (int) $item['pieces'];
$resultItems[$itemKey]['pieces_sent'] += (int) $item['pieces_sent'];
}
return $resultItems;
}
public function getItemsByUserInClosedDates(\User|int $user): ?array
{
try {
$dates = $this->getDates();
} catch (\Throwable) {
return null;
}
$today = (new \DateTimeImmutable())->format('Y-m-d');
$closedDates = array_filter($dates, fn (array $date) => $date['date_end'] < $today);
return $this->getItemsByUser($user, array_column($closedDates, 'id'));
}
public function getSettings(): array
{
if (is_string($this->preorder['settings'])) {
$this->preorder['settings'] = empty($this->preorder['settings']) ?
[] : json_decode($this->preorder['settings'], true);
}
return $this->preorder['settings'];
}
private function fetchData(): void
{
$preorder = sqlQueryBuilder()
->select('*')
->from('preorders', 'po')
->andWhere('po.id = :id')
->addParameters(['id' => $this->id], [ParameterType::INTEGER]);
try {
$preorder = $preorder->execute()->fetchAllAssociative();
} catch (\Throwable) {
$preorder = [];
}
if (count($preorder) !== 1) {
throw new NotFoundException("Preorder with id={$this->id} was not found!");
}
$this->preorder = $preorder[0];
}
public function offsetExists($offset): bool
{
return isset($this->preorder[$offset]);
}
public function offsetGet(mixed $offset): mixed
{
return $this->preorder[$offset] ?? null;
}
public function offsetSet(mixed $offset, mixed $value): void
{
throw new UnsupportedOperationException();
}
public function offsetUnset(mixed $offset): void
{
throw new UnsupportedOperationException();
}
public function toArray(): array
{
$this->getSettings();
return $this->preorder;
}
public function getPriceLevelID(?\Decimal $total = null): ?int
{
if ($total === null) {
$total = \DecimalConstants::zero();
}
$dynamicPriceLevel = $this->getActiveDynamicPrice($total);
if ($dynamicPriceLevel !== null && PreorderUtil::fieldIsComposedOfDigits($dynamicPriceLevel, 'id_price_level')) {
return (int) $dynamicPriceLevel['id_price_level'];
}
if (PreorderUtil::fieldIsComposedOfDigits($this->preorder, 'id_price_level')) {
return (int) $this->preorder['id_price_level'];
}
return null;
}
public function getPricelistID(?\Decimal $total = null): ?int
{
if ($total === null) {
$total = \DecimalConstants::zero();
}
$dynamicPricelist = $this->getActiveDynamicPrice($total);
if ($dynamicPricelist !== null && PreorderUtil::fieldIsComposedOfDigits($dynamicPricelist, 'id_pricelist')) {
return (int) $dynamicPricelist['id_pricelist'];
}
if (PreorderUtil::fieldIsComposedOfDigits($this->preorder, 'id_pricelist')) {
return (int) $this->preorder['id_pricelist'];
}
return null;
}
private function getActiveDynamicPrice(\Decimal $price): ?array
{
foreach (($this->getSettings()['dynamic_prices'] ?? []) as $dynPrice) {
if (empty($dynPrice['price_from'])) {
$overLowerBound = true;
} else {
$overLowerBound = toDecimal($dynPrice['price_from'])->lowerThanOrEqual($price);
}
if (empty($dynPrice['price_to'])) {
$underUpperBound = true;
} else {
$underUpperBound = $price->lowerThanOrEqual(toDecimal($dynPrice['price_to']));
}
if ($overLowerBound && $underUpperBound) {
return $dynPrice;
}
}
return null;
}
public function hasDynamicPrice(): bool
{
return count($this->getSettings()['dynamic_prices'] ?? []) > 0;
}
/**
* @throws DBALDriverException
* @throws DBALException
*/
public function getDates(): array
{
if ($this->dates === null) {
$qb = sqlQueryBuilder()->select('*')
->from('preorders_dates', 'pd')
->andWhere(Op::equals(['pd.id_preorder' => $this->id]));
$this->dates = $qb->execute()->fetchAllAssociative();
}
return $this->dates;
}
public function getAllowedUsersGroups(): array
{
return $this->preorder['settings']['users_groups'] ?? [];
}
public function isAllowed(\User $user): bool
{
return count(array_intersect($this->getAllowedUsersGroups(), array_keys($user->getGroups()))) > 0;
}
}