<?php
namespace App\AtechNet\CoreBundle\Entity;
use App\AtechNet\CoreBundle\Repository\StockMovementItemRepository;
use App\AtechNet\CoreBundle\Entity\PersonalizationAttributeOptionCombination;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinTable;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\OneToOne;
use Doctrine\ORM\Mapping\OrderBy;
use Doctrine\ORM\Mapping\PrePersist;
use Doctrine\ORM\Mapping\PreUpdate;
use Doctrine\ORM\Mapping\Table as Table;
/**
* StockMovementItem
*
* @Table(name="stock_movement_item")
* @Entity(repositoryClass=StockMovementItemRepository::class)
* @HasLifecycleCallbacks
*/
class StockMovementItem extends AbstractSignedEntity
{
/**
* @var int
*
* @Column(name="sort_index", type="integer")
*/
private $sortIndex = 0;
/**
* @var StockMovement
*
* @ManyToOne(targetEntity="StockMovement", inversedBy="movementItems", cascade={"persist"})
* @JoinColumn(name="stock_movement_id", referencedColumnName="id", nullable=false)
*/
private $movement;
/**
* @var ArrayCollection
*
* @ManyToMany(targetEntity="AikenShipment", inversedBy="movementItems")
* @JoinTable(name="stock_movement_items_aiken_shipments")
* @OrderBy({"created" = "DESC"})
*/
private $aikenShipments;
/**
* @var Product
*
* @ManyToOne(targetEntity="Product", inversedBy="stockMovementItems")
* @JoinColumn(name="product_id", referencedColumnName="id", nullable=true)
*/
private $product;
/**
* @var int
*
* @Column(name="quantity", type="integer")
*/
private $quantity;
/**
* @var float
*
* @Column(name="price", type="decimal", precision=12, scale=3)
*/
private $price;
/**
* @var ArrayCollection
*
* @ManyToMany(targetEntity="StockItem", inversedBy="movementItems")
* @JoinTable(
* name="stock_movement_items_stock_items",
* joinColumns={
* @JoinColumn(name="stockmovementitem_id", referencedColumnName="id", onDelete="CASCADE")
* },
* inverseJoinColumns={
* @JoinColumn(name="stockitem_id", referencedColumnName="id")
* })
* @OrderBy({"created" = "DESC"})
*/
private $items;
/**
* @var OrderItem|null
*
* @OneToOne(targetEntity="OrderItem", inversedBy="stockMovementItem", cascade={"persist", "remove"})
* @JoinColumn(name="order_item_id", referencedColumnName="id", onDelete="CASCADE")
*/
private ?OrderItem $orderItem;
/**
* @var OrderItemAttributeOption|null
*
* @OneToOne(targetEntity="OrderItemAttributeOption", inversedBy="stockMovementItem", cascade={"persist", "remove"})
* @JoinColumn(name="order_item_attribute_id", referencedColumnName="id", onDelete="CASCADE")
*/
private ?OrderItemAttributeOption $orderItemAttribute;
/**
* @var DocumentItem
*
* @OneToOne(targetEntity="DocumentItem", inversedBy="stockMovementItem", cascade={"persist", "remove"})
* @JoinColumn(name="document_item_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $documentItem;
/**
* @var string
*
* @Column(name="averege_buying_price_per_item", type="decimal", precision=10, scale=2)
*
* Average buying price of product in time of stock movement item creation
*/
private $averageBuyingPricePerItem = 0;
/**
* @var ArrayCollection
*
* @OneToMany(targetEntity="StockMovementItem", mappedBy="configChangeParentMovementItem", cascade={"persist"}, orphanRemoval=false)
* @OrderBy({"created" = "DESC"})
*/
private $configChangeChildMovementItems;
/**
* @var StockMovementItem
*
* @ManyToOne(targetEntity="StockMovementItem", inversedBy="configChangeChildMovementItems", cascade={"persist"})
* @JoinColumn(name="config_change_parent_movement_item_id", referencedColumnName="id", nullable=true)
*/
private $configChangeParentMovementItem;
/**
* @var int|null
*
* @Column(name="product_model_variation_id", type="integer", nullable=true)
*/
private ?int $productModelVariationId = 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;
/**
* @var PersonalizationAttributeOptionCombination
*
* @ManyToOne(targetEntity="PersonalizationAttributeOptionCombination", inversedBy="stockMovementItems")
* @JoinColumn(name="personalization_attribute_option_combination_id", referencedColumnName="id", nullable=true)
*/
private ?PersonalizationAttributeOptionCombination $personalizationAttributeOptionCombination;
public function __construct()
{
parent::__construct();
$this->items = new ArrayCollection();
}
/**
* @return int
*/
public function getSortIndex(): int
{
return $this->sortIndex;
}
/**
* @param int $sortIndex
*/
public function setSortIndex($sortIndex): void
{
$this->sortIndex = $sortIndex;
}
/**
* @return StockMovement
*/
public function getMovement(): StockMovement
{
return $this->movement;
}
/**
* @param StockMovement $movement
*/
public function setMovement($movement): void
{
$this->movement = $movement;
}
/**
* @return ArrayCollection
*/
public function getAikenShipments()
{
return $this->aikenShipments;
}
/**
* @param ArrayCollection $aikenShipments
*/
public function setAikenShipments($aikenShipments): void
{
$this->aikenShipments = $aikenShipments;
}
/**
* @return Product
*/
public function getProduct(): Product
{
return $this->product;
}
/**
* @param Product $product
*/
public function setProduct($product): void
{
$this->product = $product;
}
/**
* @return int
*/
public function getQuantity(): int
{
return $this->quantity;
}
/**
* @param int $quantity
*/
public function setQuantity(int $quantity): void
{
$this->quantity = $quantity;
}
/**
* @return int
*/
public function getVat(): int
{
return $this->getMovement()->getVat();
}
/**
* @return float
*/
public function getPrice(): float
{
return $this->price;
}
/**
* @param bool $homeCurrency
*
* @return string|int|float
*/
public function getPricePerItem(bool $homeCurrency = false): string|int|float
{
if ( $this->getPrice() > 0 && $this->getQuantity() > 0 ) {
if ( $homeCurrency === true && $this->getMovement()->getExchangeRate() > 0 ) {
return number_format(( $this->getPrice() / $this->getMovement()->getExchangeRate() ) / $this->getQuantity(), 2, '.', '');
}
return number_format($this->getPrice() / $this->getQuantity(), 2, '.', '');
}
return 0;
}
/**
* @return float
*/
public function getTotalPricePerItem(): float
{
$movement = $this->movement;
return self::calculateTotalPricePerItem(
(float)$this->getPrice(),
(int)$this->getQuantity(),
$movement != null ? (float)$movement->getExtraPrice() : 0.0,
$movement != null ? (int)$movement->getQuantity() : 0
);
}
/**
* Same math as getTotalPricePerItem(), callable from scalar query rows so callers
* do not have to hydrate whole movement histories to read a price.
*/
public static function calculateTotalPricePerItem(
float $price,
int $quantity,
float $movementExtraPrice,
int $movementQuantity
): float {
$total = $price > 0 && $quantity > 0 ? (float)number_format($price / $quantity, 2, '.', '') : 0.0;
if ($movementQuantity > 0) {
$total += $movementExtraPrice / $movementQuantity;
}
return $total;
}
/**
* @param float $price
*/
public function setPrice($price): void
{
$this->price = $price;
}
/**
* @return float
*/
public function getVatPrice(): float
{
return $this->getCompany()->createVatPrice($this->price, $this->getVat());
}
/**
* @return string|int
*/
public function getVatPricePerItem(): string|int
{
if ($this->vatPricePerItem) {
return $this->vatPricePerItem;
}
if ($this->getVatPrice() != 0 && $this->getQuantity() > 0) {
return number_format($this->getVatPrice() / $this->getQuantity(), 2, '.', '');
}
return 0;
}
/**
* @return ArrayCollection
*/
public function getItems()
{
return $this->items;
}
/**
* @param ArrayCollection $items
*/
public function setItems($items): void
{
$this->items = $items;
}
/**
* Add StockItem
*
* @param StockItem $item
*/
public function addItem($item): void
{
if (!$this->items->contains($item)) {
$this->items->add($item);
}
}
/**
* Remove StockItem
*
* @param StockItem $item
*/
public function removeItem($item): void
{
$this->items->removeElement($item);
}
/**
* @return ?OrderItem
*/
public function getOrderItem(): ?OrderItem
{
return $this->orderItem;
}
/**
* @param ?OrderItem $orderItem
*/
public function setOrderItem(?OrderItem $orderItem): void
{
$this->orderItem = $orderItem;
}
/**
* @return Company
*/
public function getCompany(): Company
{
return $this->getMovement()->getStock()->getCompany();
}
/**
* @return mixed
*/
public function getCurrency(): mixed
{
return $this->getCompany()->getCurrency();
}
/**
* @return ?OrderItemAttributeOption
*/
public function getOrderItemAttribute(): ?OrderItemAttributeOption
{
return $this->orderItemAttribute;
}
/**
* @param ?OrderItemAttributeOption $orderItemAttribute
*/
public function setOrderItemAttribute(?OrderItemAttributeOption $orderItemAttribute): void
{
$this->orderItemAttribute = $orderItemAttribute;
}
/**
* @return DocumentItem
*/
public function getDocumentItem(): ?DocumentItem
{
return $this->documentItem;
}
/**
* @param ?DocumentItem $documentItem
*/
public function setDocumentItem(?DocumentItem $documentItem): void
{
$this->documentItem = $documentItem;
}
/**
* @return PersonalizationAttributeOptionCombination
*/
public function getPersonalizationAttributeOptionCombination(): ?PersonalizationAttributeOptionCombination
{
return $this->personalizationAttributeOptionCombination;
}
/**
* @param ?PersonalizationAttributeOptionCombination $personalizationAttributeOptionCombination
*/
public function setPersonalizationAttributeOptionCombination(?PersonalizationAttributeOptionCombination $personalizationAttributeOptionCombination): void
{
$this->personalizationAttributeOptionCombination = $personalizationAttributeOptionCombination;
}
/**
* @return int|string
*/
public function getAverageBuyingPricePerItem(): int|string
{
return $this->averageBuyingPricePerItem;
}
/**
* @param int|string $averageBuyingPricePerItem
*/
public function setAverageBuyingPricePerItem($averageBuyingPricePerItem): void
{
$this->averageBuyingPricePerItem = $averageBuyingPricePerItem;
}
/**
* @param $childMovementItem
*
* @return void
*/
public function removeConfigChangeChildMovementItem($childMovementItem): void
{
$this->configChangeChildMovementItems->removeElement($childMovementItem);
}
/**
* @return ArrayCollection
*/
public function getConfigChangeChildMovementItems()
{
return $this->configChangeChildMovementItems;
}
/**
* @return StockMovementItem|null
*/
public function getConfigChangeParentMovementItem(): ?StockMovementItem
{
return $this->configChangeParentMovementItem;
}
/**
* @param StockMovementItem|null $configChangeParentMovementItem
*
* @return void
*/
public function setConfigChangeParentMovementItem(?StockMovementItem $configChangeParentMovementItem): void
{
$this->configChangeParentMovementItem = $configChangeParentMovementItem;
}
/**
* @return int|null
*/
public function getProductModelVariationId(): ?int
{
return $this->productModelVariationId;
}
/**
* @param int|null $productModelVariationId
*
* @return void
*/
public function setProductModelVariationId(?int $productModelVariationId): void
{
$this->productModelVariationId = $productModelVariationId;
}
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
{
$quantity = max(1, (int) $this->quantity);
$netTotal = (float) $this->price;
$netPerItem = $netTotal / $quantity;
$grossTotal = $netTotal * (1 + $vatRate / 100);
$this->pricePerItemWithoutVat = (string) round($netPerItem, 3);
$this->pricePerItemWithVat = (string) round($grossTotal / $quantity, 3);
$this->vatPricePerItem = (string) round($netPerItem * $vatRate / 100, 3);
$this->totalVatPrice = (string) round($netTotal * $vatRate / 100, 3);
$this->totalPrice = (string) round($grossTotal, 3);
}
/**
* @PrePersist
* @PreUpdate
*/
public function autoRecalculateVatFields(): void
{
if ($this->movement !== null && $this->price !== null && $this->quantity) {
$this->recalculateVatFields((float) $this->getVat());
}
}
}