<?php
namespace App\AtechNet\CoreBundle\Entity;
use App\AtechNet\CoreBundle\Repository\StockItemRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Index;
use Doctrine\ORM\Mapping\JoinColumn;
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\Table as Table;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Stock
*
* @Table(name="stock_items", indexes={
* @Index(name="search_serial_number", columns={"serial_number"}),
* @Index(name="search_aiken_code", columns={"aiken_code"}),
* @Index(name="idx_in_stock", columns={"in_stock"})
* })
* @Entity(repositoryClass="App\AtechNet\CoreBundle\Repository\StockItemRepository")
*/
class StockItem extends AbstractSignedEntity
{
public const int QUALITY_GOLD = 1;
public const int QUALITY_SILVER = 2;
public const int QUALITY_BRONZE = 3;
public const int QUALITY_PLATINUM = 5;
public const int QUALITY_NEW = 6;
public const int QUALITY_BAZAAR = 7;
public const string PREFIX_FOR_SELECTED_ITEMS = 'aiken codes selected by customer: ';
public const string SERIAL_PREFIX_AT = 'AT';
public const string SERIAL_PREFIX = 'FRB';
public const int SERIAL_SUFFIX_SIZE = 4;
public const string TEMPORARY_SERIAL_PREFIX = 'TMPR';
public const int TEMPORARY_SERIAL_SUFFIX_SIZE = 5;
public const BATTERY_STATE_NEW = 1;
public const BATTERY_STATE_USED = 10;
public const BATTERY_STATE_NO_INFO = 999;
public const array KEYBOARD_LANGUAGE_VALID_VALUE_LIST = [
'BE' => 'BEL',
'CZ' => 'CZE',
'DE' => 'DEU',
'DK' => 'DNK',
'DN' => 'DNK',
'EN' => 'GBR',
'ES' => 'ESP',
'FI' => 'FIN',
'FR' => 'FRA',
'IE' => 'GBR',
'IT' => 'ITA',
'NL' => 'NDL',
'PL' => 'POL',
'PO' => 'POL',
'PT' => 'PRT',
'RU' => 'RUS',
'SE' => 'SWE',
'SK' => 'SVK',
'SWE' => 'SWE',
'UK' => 'GBR',
'US' => 'USA',
'SW' => 'SWF'
];
/**
* @var ArrayCollection
*
* @ManyToMany(targetEntity="StockMovementItem", mappedBy="items")
* @OrderBy({"created" = "DESC"})
*/
private $movementItems;
/**
* @var AikenShipmentItem
*
* @OneToOne(targetEntity="AikenShipmentItem")
* @JoinColumn(name="aiken_shipment_item_id", referencedColumnName="id", nullable=true)
*/
private $aikenShipmentItem;
/**
* @var string
*
* @Column(name="serial_number", type="string", length=64, nullable=true)
*/
private $serialNumber;
/**
* @var string
*
* @Column(name="purchase_price", type="decimal", precision=10, scale=2)
*/
private $purchasePrice = 0;
/**
* @var string
*
* @Column(name="selling_price", type="decimal", precision=10, scale=2)
*/
private $sellingPrice = 0;
/**
* @var int
*
* @Column(name="quality", type="integer")
*/
private $quality = 0;
/**
* @var int|null
*
* @Column(name="quality_points", type="integer", nullable=true)
*/
private ?int $qualityPoints = null;
/**
* @var string
*
* @Column(name="aiken_code", type="string", length=32, nullable=true )
*/
private $aikenCode;
/**
* @var string
*
* @Column(name="note", type="text", nullable=true)
*/
private $note;
/**
* @var bool
*
* @Column(name="in_stock", type="boolean")
*/
private $inStock = true;
/**
* @var bool
*
* @Column(name="available", type="boolean")
*/
private bool $available = true;
/**
* @var ?bool
*
* @Column(name="long_i_key", type="boolean", nullable=true)
*/
private ?bool $longIKey;
/**
* @var ?bool
*
* @Column(name="keyboard_backlight", type="boolean", nullable=true)
*/
private ?bool $keyboardBacklight;
/**
* @var string
*
* @Column(name="battery_duration", type="string", length=32, nullable=true )
*/
private $batteryDuration;
/**
* @var string
*
* @Column(name="battery_percentage_1", type="string", length=32, nullable=true)
*/
private $batteryPercentage1;
/**
* @var string
*
* @Column(name="battery_percentage_2", type="string", length=32, nullable=true)
*/
private $batteryPercentage2;
/**
* @var int
*
* @Column(name="battery_cycle_count_1", type="integer", nullable=true)
*/
private $batteryCycleCount1;
/**
* @var int
*
* @Column(name="battery_cycle_count_2", type="integer", nullable=true)
*/
private $batteryCycleCount2;
/**
* @var string
*
* @Column(name="keyboard_language", type="string", length=12, nullable=true )
*/
private $keyboardLanguage;
/**
* @var bool
*
* @Column(name="pen_for_touch", type="boolean", nullable=true)
*/
private $pen_for_touchscreen;
/**
* @var bool
*
* @Column(name="trackpoint", type="boolean", nullable=true)
*/
private $trackpoint;
/**
* @var bool
*
* @Column(name="fingerprint_reader", type="boolean", nullable=false, options={"default": false})
*/
private bool $fingerprintReader = false;
/**
* @var string
*
* @Column(name="error_codes", type="string", length=256, nullable=true)
*/
private $errorCodes;
/**
* @var User
*
* @Assert\NotNull
*
* @ManyToOne(targetEntity="User", inversedBy="testedItems")
* @JoinColumn(name="tester_id", referencedColumnName="id", nullable=true)
*/
private $tester;
/**
* @var int
*
* @Column(name="sort_index", type="integer")
*/
private $sortIndex = 0;
/**
* @var Album
*
* @OneToOne(targetEntity="Album", cascade={"persist", "remove"})
* @JoinColumn(name="photo_album_id", referencedColumnName="id")
*/
private $album;
/**
* @var ArrayCollection
*
* @OneToMany(targetEntity="StockItemInStockDay", mappedBy="stockItem", orphanRemoval=true)
* @OrderBy({"created" = "DESC"})
*/
private $inStockDays;
/**
* @var ArrayCollection
*
* @OneToMany(targetEntity="StockItemPosition", mappedBy="stockItem", orphanRemoval=true)
* @OrderBy({"created" = "DESC"})
*/
private $positions;
/**
* @var StockPosition
*
* @ManyToOne(targetEntity="StockPosition")
* @JoinColumn(name="last_stock_position_id", referencedColumnName="id", nullable=true)
*/
private $lastKnownStockPosition;
public function __construct()
{
parent::__construct();
$this->movementItems = new ArrayCollection();
}
/**
* @return array
*/
public static function getQualityTitleChoices(): array
{
return [
self::QUALITY_NEW => 'New',
self::QUALITY_PLATINUM => 'Platinum',
self::QUALITY_GOLD => 'Gold',
self::QUALITY_SILVER => 'Silver',
self::QUALITY_BRONZE => 'Bronze',
self::QUALITY_BAZAAR => 'Bazaar'
];
}
public static function getQualityValue(): array
{
return [
'new' => 6,
'platinum' => 5,
'gold' => 4,
'silver' => 3,
'bronze' => 2,
'bazaar' => 1
];
}
/**
* @param $i
* @return string
*/
public static function getSerialSuffix($i): string
{
return str_pad($i, StockItem::SERIAL_SUFFIX_SIZE, '0', STR_PAD_LEFT);
}
/**
* @param $i
* @return string
*/
public static function getTemporarySerialSuffix($i): string
{
return str_pad($i, StockItem::TEMPORARY_SERIAL_SUFFIX_SIZE, '0', STR_PAD_LEFT);
}
public function getPurchasePriceCalculated()
{
return StockItemRepository::getPurchasePrices(null, $this, false);
}
/**
* @return Product
*/
public function getProduct()
{
return $this->getLastMovementItem() == null ? null : $this->getLastMovementItem()->getProduct();
}
/**
* @return Product|null
*/
public function getPreviousProduct(): ?Product
{
$movementItems = $this->getMovementItems();
if ( $movementItems->count() > 0 ) {
return $movementItems->get(1)?->getProduct();
}
return null;
}
/**
* @return StockMovementItem|null
*/
public function getLastMovementItem()
{
$movementItems = $this->getMovementItems();
return $movementItems->count() > 0 ? $movementItems->first() : null;
}
public function getLastMovementItemForGetStockItemsFunction()
{
$movementItems = $this->getMovementItems();
return $movementItems->count() > 0 ? $movementItems->last() : null;
}
/**
* @return ArrayCollection
*/
public function getMovementItems()
{
return $this->movementItems;
}
/**
* @return StockMovementItem|null
*/
public function getFirstMovementItem()
{
$movementItems = $this->getMovementItems();
return $movementItems->count() > 0 ? $movementItems->last() : null;
}
public function getFirstMovementItemForGetStockItemsFunction()
{
$movementItems = $this->getMovementItems();
return $movementItems->count() > 0 ? $movementItems->first() : null;
}
/**
* @return StockMovement|null
*/
public function getFirstMovement()
{
$movements = $this->getMovements();
if ($movements->count() > 0) {
return $movements->last();
}
return null;
}
/**
* @return ArrayCollection
*/
public function getMovements()
{
$movements = new ArrayCollection();
foreach ($this->getMovementItems() as $movementItem) {
$movement = $movementItem->getMovement();
if (!$movements->contains($movement)) {
$movements->add($movement);
}
}
return $movements;
}
/**
* @param StockMovementItem $stockMovementItem
* @return float
*/
public function getLastPurchasePrice(StockMovementItem $stockMovementItem): float
{
foreach ($this->movementItems as $index => $movementItem) {
if ($movementItem->getId() == $stockMovementItem->getId()) {
// Increase
$increaseWith = 1;
do {
// Previous Stock Movement Item
$previousStockMovementItem = $this->movementItems->get(($index + $increaseWith));
if ($previousStockMovementItem) {
// Need a In Stock Movement
$stockMovement = $previousStockMovementItem->getMovement();
if ($stockMovement->getType() == StockMovement::TYPE_IN) {
// Exchange Currency
$exchangeCurrency = $stockMovement->getMerchant()->getDomain()->getCurrencyTextCode();
// Exchange Rate
$exchangeRate = 1;
if ($exchangeCurrency !== 'EUR') {
$exchangeRate = (float)$stockMovement->getExchangeRate();
}
if ((float)$exchangeRate <= 0) {
$exchangeRate = 1;
}
// Converted Price
return round(($previousStockMovementItem->getTotalPricePerItem() / $exchangeRate), 2);
}
}
// Increase
$increaseWith++;
} while ($previousStockMovementItem);
}
}
return .0;
}
/**
* @param StockMovementItem $stockMovementItem
* @return array
*/
public function getLastPurchasePriceWithCurrency(StockMovementItem $stockMovementItem): array
{
foreach ($this->movementItems as $index => $movementItem) {
if ($movementItem->getId() == $stockMovementItem->getId()) {
// Increase
$increaseWith = 1;
do {
// Previous Stock Movement Item
$previousStockMovementItem = $this->movementItems->get(($index + $increaseWith));
if ($previousStockMovementItem) {
// Need a In Stock Movement
$stockMovement = $previousStockMovementItem->getMovement();
if ($stockMovement->getType() == StockMovement::TYPE_IN) {
return [
'currency' => $stockMovement->getMerchant()->getDomain()->getCurrencyTextCode(),
'value' => $previousStockMovementItem->getTotalPricePerItem(),
];
}
}
// Increase
$increaseWith++;
} while ($previousStockMovementItem);
}
}
return [];
}
/**
* @return AikenShipmentItem
*/
public function getAikenShipmentItem()
{
return $this->aikenShipmentItem;
}
/**
* @param AikenShipmentItem $aikenShipmentItem
*/
public function setAikenShipmentItem($aikenShipmentItem)
{
$this->aikenShipmentItem = $aikenShipmentItem;
}
/**
* @return string
*/
public function getAikenCode()
{
return $this->aikenCode;
}
/**
* @param string $aikenCode
*/
public function setAikenCode($aikenCode)
{
$this->aikenCode = $aikenCode;
}
/**
* @return string
*/
public function getSerialNumber()
{
return $this->serialNumber;
}
/**
* @param string $serialNumber
*/
public function setSerialNumber($serialNumber)
{
$this->serialNumber = $serialNumber;
}
/**
* @return string
*/
public function getPurchasePrice()
{
return $this->purchasePrice;
}
/**
* @param string $purchasePrice
*/
public function setPurchasePrice(string $purchasePrice): void
{
$this->purchasePrice = $purchasePrice;
}
/**
* @return string
*/
public function getSellingVatPricePerItem(): string
{
return $this->getCompany()->createVatPrice($this->getSellingPrice(), $this->getVat());
}
/**
* @return Company|null
*/
public function getCompany(): ?Company
{
if (!is_null($this->getLastMovement())) {
return $this->getLastMovement()->getStock()->getCompany();
}
return null;
}
/**
* @return StockMovement|null
*/
public function getLastMovement(): ?StockMovement
{
$movements = $this->getMovements();
if ($movements->count() > 0) {
return $movements->first();
}
return null;
}
/**
* @return string
*/
public function getSellingPrice(): int|string
{
return $this->sellingPrice;
}
/**
* @param string $sellingPrice
*/
public function setSellingPrice(string $sellingPrice): void
{
$this->sellingPrice = $sellingPrice;
}
/**
* @return int
*/
public function getVat(): int
{
return $this->getLastMovementItem()->getVat();
}
/**
* @return int
*/
public function getQuality(): int
{
return $this->quality;
}
/**
* @param int $quality
*/
public function setQuality(int $quality): void
{
$this->quality = $quality;
}
/**
* @return int|null
*/
public function getQualityPoints(): ?int
{
return $this->qualityPoints;
}
/**
* @param int|null $qualityPoints
* @return void
*/
public function setQualityPoints(?int $qualityPoints): void
{
$this->qualityPoints = $qualityPoints;
}
/**
* @return string|null
*/
public function getNote(): ?string
{
return $this->note;
}
/**
* @param string|null $note
*/
public function setNote(?string $note)
{
$this->note = $note;
}
/**
* @return boolean
*/
public function isInStock()
{
return $this->inStock;
}
/**
* @param boolean $inStock
*/
public function setInStock($inStock)
{
$this->inStock = $inStock;
}
/**
* @return User
*/
public function getTester()
{
return $this->tester;
}
/**
* @param User $tester
*/
public function setTester($tester)
{
$this->tester = $tester;
}
/**
* @return bool
*/
public function isAvailable()
{
return $this->available;
}
/**
* @param bool $available
*/
public function setAvailable($available)
{
$this->available = $available;
}
/**
* @return int
*/
public function getSortIndex()
{
return $this->sortIndex;
}
/**
* @param int $sortIndex
*/
public function setSortIndex($sortIndex)
{
$this->sortIndex = $sortIndex;
}
/**
* @return mixed
*/
public function getCurrency()
{
if (!is_null($this->getCompany())) {
return $this->getCompany()->getCurrency();
}
return null;
}
/**
* @return mixed|string
*/
public function getQualityString()
{
if ($this->quality == 0) {
return '-';
}
return $this->getProduct()?->getProductModel()?->isProductV2()
? self::getV2QualityChoices()[$this->quality]
: self::getQualityChoices()[$this->quality];
}
/**
* @return array
*/
public static function getQualityChoices(): array
{
return [
self::QUALITY_NEW => 'N',
self::QUALITY_PLATINUM => 'P',
self::QUALITY_GOLD => 'G',
self::QUALITY_SILVER => 'S',
self::QUALITY_BRONZE => 'B',
self::QUALITY_BAZAAR => 'C'
];
}
/**
* @return array<int, string>
*/
public static function getV2QualityChoices(): array
{
return [
self::QUALITY_NEW => 'New',
self::QUALITY_GOLD => 'A',
self::QUALITY_SILVER => 'B',
self::QUALITY_BRONZE => 'C',
self::QUALITY_BAZAAR => 'D',
];
}
/**
* @return ArrayCollection
*/
public function getInStockDays()
{
return $this->inStockDays;
}
/**
* @param ArrayCollection $inStockDays
*/
public function setInStockDays($inStockDays)
{
$this->inStockDays = $inStockDays;
}
/**
* @return bool
*/
public function hasLongIKey(): ?bool
{
return $this->longIKey == true;
}
/**
* @param ?bool $longIKey
*/
public function setLongIKey(?bool $longIKey)
{
$this->longIKey = $longIKey;
}
/**
* @return bool
*/
public function hasKeyboardBacklight(): bool
{
return $this->keyboardBacklight == true;
}
/**
* @param ?bool $keyboardBacklight
*/
public function setKeyboardBacklight(?bool $keyboardBacklight)
{
$this->keyboardBacklight = $keyboardBacklight;
}
/**
* @return string
*/
public function getBatteryDuration()
{
return $this->batteryDuration;
}
/**
* @param string $batteryDuration
*/
public function setBatteryDuration($batteryDuration)
{
$this->batteryDuration = $batteryDuration;
}
/**
* @return string|null
*/
public function getBatteryPercentage1(): ?string
{
return $this->batteryPercentage1;
}
/**
* @param string|null $batteryPercentage1
* @return $this
*/
public function setBatteryPercentage1(?string $batteryPercentage1): self
{
$this->batteryPercentage1 = $batteryPercentage1;
return $this;
}
/**
* @return string|null
*/
public function getBatteryPercentage2(): ?string
{
return $this->batteryPercentage2;
}
/**
* @param string|null $batteryPercentage2
* @return $this
*/
public function setBatteryPercentage2(?string $batteryPercentage2): self
{
$this->batteryPercentage2 = $batteryPercentage2;
return $this;
}
public function getBatteryCycleCount1(): ?int
{
return $this->batteryCycleCount1;
}
public function setBatteryCycleCount1(?int $batteryCycleCount1): self
{
$this->batteryCycleCount1 = $batteryCycleCount1;
return $this;
}
public function getBatteryCycleCount2(): ?int
{
return $this->batteryCycleCount2;
}
public function setBatteryCycleCount2(?int $batteryCycleCount): self
{
$this->batteryCycleCount2 = $batteryCycleCount;
return $this;
}
/**
* @return string
*/
public function getKeyboardLanguage()
{
return $this->keyboardLanguage;
}
/**
* @param string $keyboardLanguage
*/
public function setKeyboardLanguage($keyboardLanguage)
{
$this->keyboardLanguage = $keyboardLanguage;
}
/**
* @return string
*/
public function getErrorCodes()
{
return $this->errorCodes;
}
/**
* @param string $errorCodes
*/
public function setErrorCodes($errorCodes)
{
$this->errorCodes = $errorCodes;
}
/**
* @param string $errorCode
*
* @return bool
*/
public function hasErrorCode(string $errorCode): bool
{
$errorCodes = explode(' ', $this->errorCodes);
return in_array($errorCode, $errorCodes);
}
/**
* @return bool true if requests can be made from the last position by their group.
*/
public function lastPositionSupportsRequests(): bool
{
return $this->getLastPosition()
?->getStockPosition()
?->getStockPositionGroup()
?->supportsRequests() ?? false;
}
/**
* @return StockItemPosition|null
*/
public function getLastPosition(): ?StockItemPosition
{
return $this->getPositions()->count() > 0 ? $this->getPositions()->first() : null;
}
/**
* @return ArrayCollection
*/
public function getPositions()
{
return $this->positions;
}
/**
* @return StockItemPosition|null
*/
public function getPreviousPosition(): ?StockItemPosition
{
return $this->getPositions()->count() > 1 ? $this->getPositions()[1] : null;
}
/**
* @return bool|null
*/
public function isPenForTouchscreen(): ?bool
{
return $this->pen_for_touchscreen;
}
/**
* @param ?bool $pen_for_touchscreen
*/
public function setPenForTouchscreen(?bool $pen_for_touchscreen): void
{
$this->pen_for_touchscreen = $pen_for_touchscreen;
}
/**
* @return bool|null
*/
public function isTrackpoint(): ?bool
{
return $this->trackpoint;
}
/**
* @param ?bool $trackpoint
*/
public function setTrackpoint(?bool $trackpoint): void
{
$this->trackpoint = $trackpoint;
}
/**
* @return Album
*/
public function getAlbum()
{
return $this->album;
}
/**
* @param Album|null $album
*/
public function setAlbum(?Album $album)
{
$this->album = $album;
}
/**
* @return StockPosition|null
*/
public function getLastKnownStockPosition(): ?StockPosition
{
return $this->lastKnownStockPosition;
}
/**
* @param StockPosition|null
*
* @return void
*/
public function setLastKnownStockPosition(?StockPosition $stockPosition = null): void
{
$this->lastKnownStockPosition = $stockPosition;
}
public function getBattery1State()
{
return $this->getBatteryState($this->getBatteryPercentage1(), $this->getBatteryCycleCount1());
}
public function getBattery2State()
{
return $this->getBatteryState($this->getBatteryPercentage2(), $this->getBatteryCycleCount2());
}
private function getBatteryState(?string $percentageString, ?int $cyclesCount)
{
$percentage = str_replace('%', '', $percentageString);
$percentage = trim($percentage);
$percentage = intval($percentage);
if ($percentageString && $cyclesCount) {
if ($cyclesCount < 5 && $percentage >= 98) {
return self::BATTERY_STATE_NEW;
}
return self::BATTERY_STATE_USED;
} else {
return self::BATTERY_STATE_NO_INFO;
}
}
/**
* @return Order|null
*/
public function getOrder(): ?Order {
$order = $this->getLastMovementItem()?->getMovement()?->getOrder();
if ( $order ) {
return $order;
}
$order = $this->getLastMovementItem()?->getMovement()?->getOrderConfigChange();
if ( $order ) {
return $order;
}
foreach ( $this->getMovements() as $movement ) {
if ( $movement->getType() == StockMovement::TYPE_NEUTRAL ) {
return $movement->getOrder();
}
}
return null;
}
public function getProductModelVariationId(bool $getStockItemsFunction = false): ?int
{
$lastMovementItem = $getStockItemsFunction
? $this->getLastMovementItemForGetStockItemsFunction()
: $this->getLastMovementItem();
if (is_null($lastMovementItem)) {
return null;
}
if ($lastMovementItem->getMovement()->getType() == StockMovement::TYPE_NEUTRAL) {
$movementItems = $this->getMovementItems();
if ($movementItems->count() > 1) {
$previousMovementItem = $movementItems->get(1);
return $previousMovementItem?->getProductModelVariationId();
}
}
return $lastMovementItem->getProductModelVariationId();
}
/**
* @param bool $getStockItemsFunction
* @return PersonalizationAttributeOptionCombination|null
*/
public function getPersonalizationAttributeOptionCombination(bool $getStockItemsFunction = false): PersonalizationAttributeOptionCombination|null
{
$lastMovementItem = $getStockItemsFunction
? $this->getLastMovementItemForGetStockItemsFunction()
: $this->getLastMovementItem();
if (is_null($lastMovementItem)) {
return null;
}
if ($lastMovementItem->getMovement()->getType() == StockMovement::TYPE_NEUTRAL) {
$movementItems = $this->getMovementItems();
if ($movementItems->count() > 1) {
$previousMovementItem = $movementItems->get(1);
return $previousMovementItem?->getPersonalizationAttributeOptionCombination();
}
}
return $lastMovementItem->getPersonalizationAttributeOptionCombination();
}
/**
* @return string|null
*/
public function getReprintLanguageCode(): ?string
{
$languageCode = mb_strtoupper(trim($this->keyboardLanguage));
if ( mb_strlen($languageCode) == 3 ) {
return $languageCode;
}
if ( $languageCode === 'SK-CZ' ) {
return $languageCode;
}
return StockItem::KEYBOARD_LANGUAGE_VALID_VALUE_LIST[$languageCode] ?? null;
}
/**
* @return bool
*/
public function isFingerprintReader(): bool
{
return $this->fingerprintReader;
}
/**
* @param bool $fingerprintReader
*
* @return void
*/
public function setFingerprintReader(bool $fingerprintReader): void
{
$this->fingerprintReader = $fingerprintReader;
}
}