Newer
Older
<?php
/**
* This file contains QUI\ERP\Accounting\Article
*/
namespace QUI\ERP\Accounting;
use QUI;
use QUI\ERP\Money\Price;
use QUI\Exception;
use function floatval;
use function get_called_class;
use function is_array;
/**
* Class Article
*
* @package QUI\ERP\Accounting
*/
class Article implements ArticleInterface
{
/**
* @var array
*/
protected array $attributes = [
'id' => '',

Henning Leutz
committed
/**
* Custom fields are data which field out the customer
* This data is not used for presentation or calculation
*
* in a custom field are only allowed string and numeric values
*
* @var array
*/
protected array $customFields = [];

Henning Leutz
committed
/**
* Custom data for plugins and modules
*
* @var array
*/
protected array $customData = [];
/**
* Should the price displayed?
* default = yes
*/
protected bool $displayPrice = true;
/**
* @var bool
*/
protected bool $calculated = false;
* @var float|int|null
protected float | int | null $nettoPriceNotRounded = null;
* The calculated netto sum with quantity and discount
* @var float|int
protected float | int $nettoSum = 0;
* Sum from the article, without discount and with quantity
*
* @var float|int
*/
protected float | int $nettoSubSum = 0;
/**
* The article netto price, without discount, without quantity
* comes from article
*
* @var float|int
*/
protected float | int $nettoPrice = 0;
/**
* The article netto price, without discount, without quantity
* comes from calc
*
* @var float|int
*/
protected float | int $nettoBasisPrice = 0;
protected mixed $vatArray = [];
/**
protected mixed $isEuVat;
/**
protected mixed $isNetto;
/**
* @var ArticleDiscount|null
*/
protected ?ArticleDiscount $Discount = null;
/**
* @var null|QUI\Interfaces\Users\User
*/
protected ?QUI\Interfaces\Users\User $User = null;
* @var null|QUI\ERP\Currency\Currency
protected ?QUI\ERP\Currency\Currency $Currency = null;
/**
* Article constructor.
*
* @param array $attributes - (id, articleNo, title, description, unitPrice, nettoPriceNotRounded, quantity, discount, customData)
* @throws Exception
public function __construct(array $attributes = [])
'uuid',
'productSetParentUuid',
'nettoPriceNotRounded', // optional
foreach ($defaults as $key) {
if (isset($attributes[$key])) {
$this->attributes[$key] = $attributes[$key];
}
if (isset($attributes['vat']) && $attributes['vat'] !== '') {
$this->attributes['vat'] = $attributes['vat'];
if (isset($attributes['position'])) {
$this->position = (float)$attributes['position'];
}
if (isset($attributes['discount'])) {
$this->Discount = ArticleDiscount::unserialize($attributes['discount']);
$this->Discount?->setArticle($this);
if (isset($attributes['calculated'])) {
$calc = $attributes['calculated'];
if (isset($calc['nettoPriceNotRounded'])) {
$this->nettoPriceNotRounded = $calc['nettoPriceNotRounded'];
$this->price = $calc['price'];
$this->basisPrice = $calc['basisPrice'];
$this->sum = $calc['sum'];
$this->nettoPrice = $calc['nettoPrice'];
$this->nettoBasisPrice = $calc['nettoBasisPrice'];
$this->nettoSum = $calc['nettoSum'];
if (isset($calc['nettoSubSum'])) {
$this->nettoSubSum = $calc['nettoSubSum'];
}
$this->vatArray = $calc['vatArray'];
$this->isEuVat = $calc['isEuVat'];
$this->isNetto = $calc['isNetto'];
$this->calculated = true;

Henning Leutz
committed
if (isset($attributes['customFields']) && is_array($attributes['customFields'])) {

Henning Leutz
committed
$this->customFields = $attributes['customFields'];
}
if (isset($attributes['customData']) && is_array($attributes['customData'])) {
$this->customData = $attributes['customData'];
}
if (isset($attributes['displayPrice'])) {
$this->displayPrice = (bool)$attributes['displayPrice'];
}
if (isset($attributes['currency'])) {
try {
$this->Currency = QUI\ERP\Currency\Handler::getCurrency($attributes['currency']);
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);
}
}
if (!$this->Currency) {
$this->Currency = QUI\ERP\Currency\Handler::getDefaultCurrency();
}
/**
* Return the article view
*
* @return ArticleView
*/
public function getView(): ArticleView
/**
* Return the Article ID
*

Henning Leutz
committed
* @return int
public function getId(): int
{
if (isset($this->attributes['id'])) {

Henning Leutz
committed
return (int)$this->attributes['id'];

Henning Leutz
committed
return 0;
/**
* Return the Article UUID
*
* @return string|null
*/
public function getUuid(): ?string
{
return !empty($this->attributes['uuid']) ? $this->attributes['uuid'] : null;
}
/**
* Return the UUID of a parent article, if this article is part of a product set.
*
* @return string|null
*/
public function getProductSetParentUuid(): ?string
{
return !empty($this->attributes['productSetParentUuid']) ? $this->attributes['productSetParentUuid'] : null;
}
/**
* Return the Article Number
*
* @return int|string
public function getArticleNo(): int | string
{
if (isset($this->attributes['articleNo'])) {
return $this->attributes['articleNo'];
}
return '';
}
/**
* Return the GTIN Number, if the article has one
*
* @return string
*/
public function getGTIN(): string
{
if (isset($this->attributes['gtin'])) {
return $this->attributes['gtin'];
}
return '';
}
/**
* Returns the article title
*
* @return string
*/
public function getTitle(): string
{
if (isset($this->attributes['title'])) {
return $this->attributes['title'];
}
return '';
}

Henning Leutz
committed
/**
* Return the article image
*
* @return null|QUI\Projects\Media\Image
*/
public function getImage(): ?QUI\Projects\Media\Image

Henning Leutz
committed
{
if (isset($this->attributes['image'])) {
try {
return QUI\Projects\Media\Utils::getImageByUrl(
$this->attributes['image']
);
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);
}
}
$Product = null;
try {
$Product = QUI\ERP\Products\Handler\Products::getProductByProductNo(
$this->getArticleNo()
);
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);
}
if (!empty($Product) && method_exists($Product, 'getImage')) {

Henning Leutz
committed
try {
return $Product->getImage();
} catch (QUI\Exception) {

Henning Leutz
committed
}
}
try {
$Project = QUI::getRewrite()->getProject();
$PlaceholderImage = $Project->getMedia()->getPlaceholderImage();

Henning Leutz
committed
if ($PlaceholderImage instanceof QUI\Projects\Media\Image) {

Henning Leutz
committed
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);
}
return null;
}
/**
* Returns the article description
*
* @return string
*/
public function getDescription(): string
{
if (isset($this->attributes['description'])) {
return $this->attributes['description'];
}
return '';
}
/**
* Returns the article unit price
*
public function getUnitPrice(): Price
if (isset($this->attributes['unitPrice'])) {
$unitPrice = $this->attributes['unitPrice'];
return new Price($unitPrice, $this->Currency);
/**
* Returns the article unit price
*
* @return QUI\ERP\Money\Price
*/
public function getUnitPriceUnRounded(): Price
if (isset($this->attributes['nettoPriceNotRounded'])) {
return new Price($this->attributes['nettoPriceNotRounded'], $this->Currency);
}
if ($this->nettoPriceNotRounded !== null) {
return new Price($this->nettoPriceNotRounded, $this->Currency);
if (isset($this->attributes['unitPrice'])) {
$this->nettoPriceNotRounded = $this->attributes['unitPrice'];
}
return new Price($this->nettoPriceNotRounded, $this->Currency);
}
/**
* Returns the article total sum
*
public function getSum(): Price
{
$this->calc();
return new Price($this->sum, $this->Currency);
}
/**
* Return the VAT for the article
*
* @return float|int
public function getVat(): float | int
if (isset($this->attributes['vat']) && $this->attributes['vat'] !== '') {

Henning Leutz
committed
return (float)$this->attributes['vat'];
// check if product exists and has a vat
if (!empty($this->attributes['id'])) {
try {
$Area = null;
if ($this->getUser()) {
$Area = QUI\ERP\Areas\Utils::getAreaByCountry($this->getUser()->getCountry());
}
if (!$Area) {
$Area = QUI\ERP\Defaults::getArea();
}
$Product = QUI\ERP\Products\Handler\Products::getProduct((int)$this->attributes['id']);
$Vat = $Product->getField(QUI\ERP\Products\Handler\Fields::FIELD_VAT);
$TaxType = new QUI\ERP\Tax\TaxType($Vat->getValue());
$TaxEntry = TaxUtils::getTaxEntry($TaxType, $Area);
return $TaxEntry->getValue();
} catch (QUI\Exception $Exception) {
QUI\System\Log::addDebug($Exception->getMessage());
}
}
try {
if ($this->getUser()) {
return QUI\ERP\Tax\Utils::getTaxByUser($this->getUser())->getValue();
}
// return default vat
$Area = QUI\ERP\Defaults::getArea();
$TaxType = QUI\ERP\Tax\Utils::getTaxTypeByArea($Area);
$TaxEntry = QUI\ERP\Tax\Utils::getTaxEntry($TaxType, $Area);
return $TaxEntry->getValue();
} catch (QUI\Exception $Exception) {
QUI\System\Log::addCritical($Exception->getMessage());
QUI\System\Log::writeException($Exception);
return 0;
}
}
/**
* @return null|QUI\Interfaces\Users\User
public function getUser(): ?QUI\Interfaces\Users\User
return $this->User;
}
/**
* Set the user to the product, this user will be used for the calculation
*
* @param QUI\Interfaces\Users\User $User
*/
public function setUser(QUI\Interfaces\Users\User $User): void
$this->calculated = false;
/**
* Return the currency of the article
*
* @return QUI\ERP\Currency\Currency|null
*/
public function getCurrency(): ?QUI\ERP\Currency\Currency
{
return $this->Currency;
}
/**
* Set the currency for the article
*
* @param QUI\ERP\Currency\Currency $Currency
*/
public function setCurrency(QUI\ERP\Currency\Currency $Currency): void
{
$this->Currency = $Currency;
}
/**
* Returns the article quantity
*
* @return float|int|bool
public function getQuantity(): float | int | bool
{
if (isset($this->attributes['quantity'])) {
return $this->attributes['quantity'];
}
return 1;
}
/**
* Returns the article quantity
*
* @param null|QUI\Locale $Locale
* @return string
*/
public function getQuantityUnit(null | QUI\Locale $Locale = null): string
{
if ($Locale === null) {
$Locale = QUI::getLocale();
}
try {
if (empty($this->attributes['quantityUnit']['id'])) {
return '';
}
$current = $Locale->getCurrent();
$unitId = $this->attributes['quantityUnit']['id'];
$UnitField = QUI\ERP\Products\Handler\Fields::getField(QUI\ERP\Products\Handler\Fields::FIELD_UNIT);
$options = $UnitField->getOptions();
if (isset($options['entries'][$unitId])) {
$titles = $options['entries'][$unitId]['title'];
if (!empty($titles[$current])) {
} catch (QUI\Exception) {
}
return $this->attributes['quantityUnit']['title'];
}
/**
* Return the price from the article
*
* @return Price
*/
public function getPrice(): Price
{
$this->calc();
return new Price($this->sum, $this->Currency);
/**
* @return bool
*/
public function displayPrice(): bool
{
return $this->displayPrice;
}
* @param float|int $discount
* @param int $discountType - default = complement
*
* @todo überdenken, ganzer artikel ist eigentlich nicht änderbar
*/
public function setDiscount(float | int $discount, int $discountType = Calc::CALCULATION_COMPLEMENT): void
{
switch ($discountType) {
case Calc::CALCULATION_PERCENTAGE:
case Calc::CALCULATION_COMPLEMENT:
break;
default:
$discountType = Calc::CALCULATION_COMPLEMENT;
}
$this->Discount = new ArticleDiscount($discount, $discountType);
}
/**
* Return the current discount
*
* @return ArticleDiscount|null
*/
public function getDiscount(): ?ArticleDiscount
$this->Discount?->setArticle($this);

Henning Leutz
committed
return $this->Discount;
}
/**
* Has the article a discount
*
* @return bool
*/
public function hasDiscount(): bool
{
return !!$this->getDiscount();
}
//endregion
/**
* @param null|Calc|QUI\ERP\User $Instance
* @return self
*/
public function calc(null | Calc | QUI\ERP\User $Instance = null): Article
{
if ($this->calculated) {
return $this;
}
if ($Instance instanceof QUI\ERP\User) {
$Calc = Calc::getInstance($Instance);
} elseif ($Instance instanceof Calc) {
$Calc = $Instance;
} else {
$Calc = Calc::getInstance();
if ($this->getUser()) {
$Calc->setUser($this->getUser());
}
$Calc->calcArticlePrice($this, function ($data) {
$self = $this;
$self->price = $data['price'];
$self->sum = $data['sum'];
if (isset($data['nettoPriceNotRounded'])) {
$self->nettoPriceNotRounded = $data['nettoPriceNotRounded'];
$self->basisPrice = $data['basisPrice'];
$self->nettoPrice = $data['nettoPrice'];
$self->nettoBasisPrice = $data['nettoBasisPrice'];
$self->nettoSubSum = $data['nettoSubSum'];
$self->nettoSum = $data['nettoSum'];
$self->vatArray = $data['vatArray'];
$self->isEuVat = $data['isEuVat'];
$self->isNetto = $data['isNetto'];
$self->calculated = true;
});
return $this;
}
/**
* Return the article as an array
*
* @return array
*/
public function toArray(): array
if (isset($this->attributes['vat']) && $this->attributes['vat'] !== '') {
if ($this->hasDiscount()) {
$discount = $this->Discount->toJSON();
}
if (!empty($this->attributes['control'])) {
$class = $this->attributes['control'];
}
$quantityUnit = '';
if (isset($this->attributes['quantityUnit'])) {
$quantityUnit = $this->attributes['quantityUnit'];
}
'uuid' => $this->getUuid(),
'productSetParentUuid' => $this->getProductSetParentUuid(),
'title' => $this->getTitle(),
'articleNo' => $this->getArticleNo(),
'gtin' => $this->getGTIN(),
'description' => $this->getDescription(),
'unitPrice' => $this->getUnitPrice()->value(),
'displayPrice' => $this->displayPrice(),
'quantity' => $this->getQuantity(),
'sum' => $this->getSum()->value(),
'vat' => $vat,
'discount' => $discount,
'control' => $this->attributes['control'],
'class' => $class,
'customFields' => $this->customFields,
'customData' => $this->customData,
'calculated' => [
'price' => $this->price,
'basisPrice' => $this->basisPrice,
'nettoPriceNotRounded' => $this->nettoPriceNotRounded,
'sum' => $this->sum,
'nettoPrice' => $this->nettoPrice,
'nettoBasisPrice' => $this->nettoBasisPrice,
'nettoSubSum' => $this->nettoSubSum,
'nettoSum' => $this->nettoSum,
'vatArray' => $this->vatArray,
'isEuVat' => $this->isEuVat,
'isNetto' => $this->isNetto

Henning Leutz
committed
//region custom fields
/**
* Return an article custom field

Henning Leutz
committed
*
* @param string $key
* @return mixed|null
*/
public function getCustomField(string $key): mixed

Henning Leutz
committed
{
if (isset($this->customFields[$key])) {
return $this->customFields[$key];
}
return null;
}
/**
* @return array
*/
public function getCustomFields(): array

Henning Leutz
committed
{
return $this->customFields;
}
/**
* @return array
*/
public function getCustomData(): array
{
return $this->customData;
}

Henning Leutz
committed
//endregion