<?php
namespace App\AtechNet\CoreBundle\Entity;
use App\AtechNet\CoreBundle\Enum\ServicingPosition;
use App\AtechNet\CoreBundle\Service\Order\OrderService;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\PrePersist;
use Doctrine\ORM\Mapping\PreUpdate;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\OneToOne;
use Doctrine\ORM\Mapping\OrderBy;
use Doctrine\ORM\Mapping\Table as Table;
use Symfony\Component\Validator\Constraints as Assert;
/**
* OrderItem
*
* @Table(name="orders__items")
* @Entity(repositoryClass="App\AtechNet\CoreBundle\Repository\OrderItemRepository")
* @HasLifecycleCallbacks
*/
class OrderItem extends AbstractEntity
{
/**
* @var Order
*
* @Assert\NotNull
*
* @ManyToOne(targetEntity="Order", inversedBy="orderItems")
* @JoinColumn(name="order_id", referencedColumnName="id")
*/
private $order;
/**
* @var Product
*
* @ManyToOne(targetEntity="Product", inversedBy="orderItems")
* @JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
/**
* @var string
*
* @Column(name="custom_configuration", type="text", nullable=true)
*/
private $customConfiguration;
/**
* @var string
*
* @Column(name="price", type="decimal", precision=12, scale=3)
*/
private $price;
/**
* @var int
*
* @Column(name="quantity", type="integer")
*/
private $quantity;
/**
* @var int
*
* @Column(name="position", type="integer")
*/
private $position = 0;
/**
* @var bool
*
* @Column(name="from_timed_sale", type="boolean")
*/
private $fromTimedSale = false;
/**
* @var ArrayCollection
*
* @OneToMany(targetEntity="OrderItemAttributeOption", mappedBy="orderItem", orphanRemoval=true, cascade={"persist", "remove"})
* @OrderBy({"created" = "ASC"})
**/
private $itemAttributes;
/**
* @var OrderItem|null
*
* @ManyToOne(targetEntity="OrderItem", inversedBy="children", cascade={"persist"})
* @JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE", nullable=true)
*/
private $parent;
/**
* @var ArrayCollection
*
* @OneToMany(targetEntity="OrderItem", mappedBy="parent", cascade={"persist", "remove"})
**/
private $children;
/**
* @var StockMovementItem|null
*
* @OneToOne(targetEntity="StockMovementItem", mappedBy="orderItem", cascade={"persist", "remove"})
*/
private $stockMovementItem;
/**
* @var PersonalizationAttributeOptionCombination|null
*
* @ManyToOne(targetEntity="PersonalizationAttributeOptionCombination", inversedBy="orderItems")
* @JoinColumn(name="personalization_attribute_option_combination_id", referencedColumnName="id", nullable=true)
*/
private ?PersonalizationAttributeOptionCombination $personalizationAttributeOptionCombination = null;
/**
* @var ProductContainer|null
*
* @ManyToOne(targetEntity="ProductContainer")
* @JoinColumn(name="product_container_id", referencedColumnName="id", nullable=true)
*/
private $productContainer;
/**
* @var ProductVariation|null
*
* @Assert\NotNull
*
* @ManyToOne(targetEntity="ProductVariation", inversedBy="orderItems")
* @JoinColumn(name="product_variation_id", referencedColumnName="id", nullable=true)
*/
private ?ProductVariation $productVariation = null;
/**
* @var ?ProductMarketplaceListing
*
* @ManyToOne(targetEntity="ProductMarketplaceListing", inversedBy="orderItems")
* @JoinColumn(name="marketplace_listing_id", referencedColumnName="id", nullable=true)
*/
private ?ProductMarketplaceListing $productMarketplaceListing = null;
/**
* @var int|null
*
* @Column(name="product_model_variation_id", type="integer", nullable=true)
*/
private ?int $productModelVariationId = null;
/**
* @var int|null
*
* @Column(name="product_model_variation_marketplace_listing_id", type="integer", nullable=true)
*/
private ?int $productModelVariationMarketplaceListingId = null;
/**
* @var string|null
*
* @Column(name="vat_price_per_item", type="decimal", precision=12, scale=3, nullable=true)
*/
private ?string $vatPricePerItem = null;
/**
* @var string|null
*
* @Column(name="total_vat_price", type="decimal", precision=12, scale=3, nullable=true)
*/
private ?string $totalVatPrice = null;
/**
* @var string|null
*
* @Column(name="total_price", type="decimal", precision=12, scale=3, nullable=true)
*/
private ?string $totalPrice = null;
/**
* @var string|null
*
* @Column(name="price_per_item", type="decimal", precision=12, scale=3, nullable=true)
*/
private ?string $pricePerItemWithoutVat = null;
/**
* @var string|null
*
* @Column(name="price_per_item_with_vat", type="decimal", precision=12, scale=3, nullable=true)
*/
private ?string $pricePerItemWithVat = null;
public function __construct()
{
parent::__construct();
$this->children = new ArrayCollection();
$this->itemAttributes = new ArrayCollection();
}
/**
* @return Order
*/
public function getOrder()
{
return $this->order;
}
/**
* @param Order $order
*/
public function setOrder($order)
{
$this->order = $order;
}
/**
* Set price
*
* @param string $price
* @return OrderItem
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* Get price
*
* @return string
*/
public function getPrice()
{
if (in_array($this->order->getDomain()->getId(), [Domain::DOMAIN_ID_RT_HU, Domain::DOMAIN_ID_WS_HU])
&& $this->order->getVat() == 0
&& $this->price % 10 != 0) {
return ceil($this->price / 10) * 10;
}
return $this->price;
}
/**
* Set quantity
*
* @param int $quantity
* @return OrderItem
*/
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
/**
* Get quantity
*
* @return int
*/
public function getQuantity()
{
return $this->quantity;
}
public function increaseQuantity($val = 1)
{
$this->quantity = $this->quantity + $val;
}
/**
* Set position
*
* @param int $position
* @return OrderItem
*/
public function setPosition($position)
{
$this->position = $position;
return $this;
}
/**
* Get position
*
* @return int
*/
public function getPosition()
{
return $this->position;
}
/**
* @return Product
*/
public function getProduct()
{
return $this->product;
}
/**
* @param Product $product
*/
public function setProduct($product)
{
$this->product = $product;
}
/**
* @return ArrayCollection
*/
public function getItemAttributes()
{
return $this->itemAttributes;
}
/**
* @param ArrayCollection $itemAttributes
*/
public function setItemAttributes($itemAttributes)
{
$this->itemAttributes = $itemAttributes;
}
/**
* @param OrderItemAttributeOption $itemAttributes
*/
public function addItemAttribute(OrderItemAttributeOption $itemAttribute)
{
$this->itemAttributes->add($itemAttribute);
}
/**
* @return void
*/
public function reMakeArrayCollectionsForOrderItems()
{
$this->children = new ArrayCollection();
$this->itemAttributes = new ArrayCollection();
}
/**
* @return OrderItem
*/
public function getParent()
{
return $this->parent;
}
/**
* @param OrderItem|null $parent
*/
public function setParent(?OrderItem $parent): void
{
$this->parent = $parent;
}
/**
* @return ArrayCollection
*/
public function getChildren()
{
return $this->children;
}
/**
* @param ArrayCollection $children
*/
public function setChildren($children)
{
$this->children = $children;
}
/**
* @return StockMovementItem|null
*/
public function getStockMovementItem()
{
return $this->stockMovementItem;
}
/**
* @param StockMovementItem|null $stockMovementItem
*/
public function setStockMovementItem(?StockMovementItem $stockMovementItem)
{
$this->stockMovementItem = $stockMovementItem;
}
/**
* @return PersonalizationAttributeOptionCombination|null
*/
public function getPersonalizationAttributeOptionCombination(): ?PersonalizationAttributeOptionCombination
{
return $this->personalizationAttributeOptionCombination;
}
/**
* @param PersonalizationAttributeOptionCombination|null $personalizationAttributeOptionCombination
*/
public function setPersonalizationAttributeOptionCombination(?PersonalizationAttributeOptionCombination $personalizationAttributeOptionCombination): void
{
$this->personalizationAttributeOptionCombination = $personalizationAttributeOptionCombination;
}
/**
* @return ProductContainer
*/
public function getProductContainer()
{
return $this->productContainer;
}
/**
* @param ProductContainer|null $productContainer
*/
public function setProductContainer(?ProductContainer $productContainer): void
{
$this->productContainer = $productContainer;
}
/**
* Returns attributeOption Ids for current OrderItem
*
* @return int|string
*/
public function getItemAttributeIDs()
{
$itemAttributeOptions = $this->itemAttributes;
$ids = 'none';
if ($itemAttributeOptions->count() > 0) {
/**
* @var OrderItemAttributeOption $oiao
*/
foreach ($itemAttributeOptions as $oiao) {
if ($ids == 'none') {
$ids = $oiao->getAttributeOption()->getId();
} else {
$ids .= ',' . $oiao->getAttributeOption()->getId();
}
}
}
return $ids;
}
/**
* Returns the price of this OrderItem with attributes, on current domain, with VAT
*
* @return float
*/
public function getItemPriceForOnePiece()
{
$price = $this->getPrice();
/**
* @var OrderItemAttributeOption $oiao
*/
foreach ($this->getItemAttributes() as $oiao) {
$price = $price + $oiao->getPrice();
}
return round($price, 2);
}
/**
* Returns Full price of this OrderItem
*
* @return int
*/
public function getItemFullPrice()
{
$fullPrice = $this->quantity * $this->getItemPriceForOnePiece();
return round($fullPrice, 2);
}
/**
* @return bool
*/
public function isFromTimedSale()
{
return $this->fromTimedSale;
}
/**
* @param bool $fromTimedSale
*/
public function setFromTimedSale($fromTimedSale)
{
$this->fromTimedSale = $fromTimedSale;
}
/**
* @return string|null
*/
public function getCustomConfiguration(): ?string
{
return $this->customConfiguration;
}
/**
* @param string|null $customConfiguration
* @return void
*/
public function setCustomConfiguration(?string $customConfiguration): void
{
$this->customConfiguration = $customConfiguration;
}
/**
* @return bool
*/
public function isMainProduct(): bool
{
return in_array($this->getProduct()->getType()->getId(), ProductType::getMainProductTypeIds());
}
/**
* @return ProductVariation|null
*/
public function getProductVariation(): ?ProductVariation
{
return $this->productVariation;
}
/**
* @param ProductVariation|null $productVariation
* @return void
*/
public function setProductVariation(?ProductVariation $productVariation): void
{
$this->productVariation = $productVariation;
}
/**
* @return ProductMarketplaceListing|null
*/
public function getProductMarketplaceListing(): ?ProductMarketplaceListing
{
return $this->productMarketplaceListing;
}
/**
* @param ProductMarketplaceListing|null $productMarketplaceListing
* @return void
*/
public function setProductMarketplaceListing(?ProductMarketplaceListing $productMarketplaceListing): void
{
$this->productMarketplaceListing = $productMarketplaceListing;
}
public function getProductModelVariationId(): ?int
{
return $this->productModelVariationId;
}
public function setProductModelVariationId(?int $productModelVariationId): void
{
$this->productModelVariationId = $productModelVariationId;
}
public function getProductModelVariationMarketplaceListingId(): ?int
{
return $this->productModelVariationMarketplaceListingId;
}
public function setProductModelVariationMarketplaceListingId(?int $productModelVariationMarketplaceListingId): void
{
$this->productModelVariationMarketplaceListingId = $productModelVariationMarketplaceListingId;
}
public function getVatPricePerItem(): ?string
{
return $this->vatPricePerItem;
}
public function setVatPricePerItem(?string $vatPricePerItem): void
{
$this->vatPricePerItem = $vatPricePerItem;
}
public function getTotalVatPrice(): ?string
{
return $this->totalVatPrice;
}
public function setTotalVatPrice(?string $totalVatPrice): void
{
$this->totalVatPrice = $totalVatPrice;
}
public function getTotalPrice(): ?string
{
return $this->totalPrice;
}
public function setTotalPrice(?string $totalPrice): void
{
$this->totalPrice = $totalPrice;
}
public function getPricePerItemWithoutVat(): ?string
{
return $this->pricePerItemWithoutVat;
}
public function setPricePerItemWithoutVat(?string $pricePerItemWithoutVat): void
{
$this->pricePerItemWithoutVat = $pricePerItemWithoutVat;
}
public function getPricePerItemWithVat(): ?string
{
return $this->pricePerItemWithVat;
}
public function setPricePerItemWithVat(?string $pricePerItemWithVat): void
{
$this->pricePerItemWithVat = $pricePerItemWithVat;
}
public function recalculateVatFields(float $vatRate): void
{
$grossPerItem = (float) $this->price;
$quantity = max(1, (int) $this->quantity);
$netPerItem = $vatRate > 0 ? $grossPerItem / (1 + $vatRate / 100) : $grossPerItem;
$this->pricePerItemWithoutVat = (string) round($netPerItem, 3);
$this->pricePerItemWithVat = (string) round($grossPerItem, 3);
$this->vatPricePerItem = (string) round($grossPerItem - $netPerItem, 3);
$this->totalVatPrice = (string) round(($grossPerItem - $netPerItem) * $quantity, 3);
$this->totalPrice = (string) round($grossPerItem * $quantity, 3);
}
/**
* @PrePersist
* @PreUpdate
*/
public function autoRecalculateVatFields(): void
{
$vat = $this->order?->getVat();
if ($vat !== null && $this->price !== null && $this->quantity) {
$this->recalculateVatFields((float) $vat);
}
}
private static function mapAttributeToComparison(AttributeOption $option)
{
$attribute = $option->getAttribute();
return [
'attributeId' => $attribute->getId(),
'attributeTitle' => $attribute->getTitle(),
'optionTitle' => $option->getTitle(),
];
}
public function recommendedPosition(array $stockItems, array $variationOptions): ServicingPosition
{
$productOptionsComp = array_map(
fn (ProductAttribute $attr) => self::mapAttributeToComparison($attr->getOption()),
$this->getProduct()->getConfigurationAttributes());
$variationOptionsComp = array_map(
fn (AttributeOption $option) => self::mapAttributeToComparison($option),
$variationOptions);
$comparedOptions = $this->compareCommonOptions($productOptionsComp, $variationOptionsComp);
$attributeIds = array_map(
fn (OrderItemAttributeOption $option) => $option->getAttributeOption()->getAttribute()->getId(),
$this->itemAttributes->toArray()
);
// Quality may vary
$qualityMayVary = 'no';
if ( !is_null($this->order->getQualityMayVarySetting()) ) {
$qualityMayVary = $this->order->getQualityMayVarySetting()->getValue();
}
if ( $qualityMayVary == 'no' ) {
/** @var StockItem $stockItem */
foreach ( $this->getStockMovementItem()?->getItems() ?? [] as $stockItem ) {
if (( StockItem::getQualityTitleChoices()[$stockItem->getQuality()] ?? '' ) !== ( $variationOptions[Attribute::ATTRIBUTE_ID_QUALITY] ?? null )?->getTitle() ) {
// Quality Values
$qualityA = StockItem::getQualityValue()[strtolower(( StockItem::getQualityTitleChoices()[$stockItem->getQuality()] ?? '' ))] ?? 0;
$qualityB = StockItem::getQualityValue()[strtolower(( $variationOptions[Attribute::ATTRIBUTE_ID_QUALITY] ?? null )?->getTitle())] ?? 0;
// Compare
$comparison = $qualityA <=> $qualityB;
if ( $comparison == 1 ) {
return ServicingPosition::ADMIN_VARO;
}
if ( $comparison == -1 ) {
return ServicingPosition::FOLIAZAS_VARO;
}
}
}
}
// ha NEW a quality akkor egybol CSOMAGOLAS, kivetel a fotermek
$product = $this->getProduct();
if ($product->getQualityStatus() === Product::QualityStatus_New && !$product->getType()->isMainProductType()) {
return ServicingPosition::CSOMAGOLAS;
}
// ha telleg valtozott a konfig (van variacio) es mas a variacio mint a konfig, - CONFIG-VARO
if ($comparedOptions['differences'] !== []) {
return ServicingPosition::CONFIG_VARO;
}
// vagy billt kell festeni - CONFIG-VARO
if (in_array(184, $attributeIds)) {
return ServicingPosition::CONFIG_VARO;
}
// minden mas TELEPITES_VARO
return ServicingPosition::TELEPITES_VARO;
}
private function compareCommonOptions(array $prodOptions, array $varOptions): array
{
// Create lookups by attributeId
$prodLookup = [];
foreach ($prodOptions as $option) {
$prodLookup[$option['attributeId']] = $option;
}
$varLookup = [];
foreach ($varOptions as $option) {
$varLookup[$option['attributeId']] = $option;
}
// Find common attributeIds
$commonAttributeIds = array_intersect(array_keys($prodLookup), array_keys($varLookup));
// Compare optionTitles for common attributeIds
$differences = [];
foreach ($commonAttributeIds as $attributeId) {
$prodOption = $prodLookup[$attributeId];
$varOption = $varLookup[$attributeId];
if ($prodOption['optionTitle'] !== $varOption['optionTitle']) {
$differences[] = [
'attributeId' => $attributeId,
'attributeTitle' => $prodOption['attributeTitle'],
'prodOptionTitle' => $prodOption['optionTitle'],
'varOptionTitle' => $varOption['optionTitle']
];
}
}
return [
'common_attribute_ids' => $commonAttributeIds,
'differences' => $differences
];
}
}