Доработки `Item`

- поддержка `excise`, покрыта тестами
- фикс `setVat()`
- улучшен `jsonSerialize()`
pull/10/head
Anthony Axenov 2021-12-02 00:23:37 +08:00
parent 650b46923e
commit 11646113b6
3 changed files with 134 additions and 15 deletions

View File

@ -20,6 +20,7 @@ use AtolOnline\{
Exceptions\InvalidDeclarationNumberException,
Exceptions\InvalidEnumValueException,
Exceptions\InvalidOKSMCodeException,
Exceptions\NegativeItemExciseException,
Exceptions\NegativeItemPriceException,
Exceptions\NegativeItemQuantityException,
Exceptions\TooHighItemQuantityException,
@ -28,7 +29,8 @@ use AtolOnline\{
Exceptions\TooLongItemNameException,
Exceptions\TooLongMeasurementUnitException,
Exceptions\TooLongUserdataException,
Exceptions\TooManyException};
Exceptions\TooManyException
};
/**
* Предмет расчёта (товар, услуга)
@ -92,6 +94,11 @@ class Item extends Entity
*/
protected ?string $user_data = null;
/**
* @var float|null Сумма акциза, включенная в стоимость (1229)
*/
protected ?float $excise = null;
/**
* @var string|null Цифровой код страны происхождения товара (1230)
*/
@ -214,14 +221,14 @@ class Item extends Entity
}
/**
* Возвращает стоимость
* Возвращает стоимость (цена * количество + акциз)
*
* @return float
* @throws TooHighSumException
*/
public function getSum(): float
{
$sum = $this->price * $this->quantity;
$sum = $this->getPrice() * $this->getQuantity() + (float)$this->getExcise();
if ($sum > Constraints::MAX_COUNT_ITEM_PRICE) {
throw new TooHighSumException($this->getName(), $sum);
}
@ -321,16 +328,19 @@ class Item extends Entity
* @param Vat|string|null $vat Объект ставки, одно из значений VatTypes или null для удаления ставки
* @return $this
* @throws TooHighSumException
* @throws InvalidEnumValueException
*/
public function setVat(Vat|string|null $vat): self
{
if (is_string($vat)) {
$vat = trim($vat);
VatTypes::isValid($vat) && $vat = new Vat($vat, $this->getSum());
empty($vat)
? $this->vat = null
: VatTypes::isValid($vat) && $this->vat = new Vat($vat, $this->getSum());
} elseif ($vat instanceof Vat) {
$vat->setSum($this->getSum());
$this->vat = $vat;
}
$this->vat = $vat ?: null;
return $this;
}
@ -405,6 +415,32 @@ class Item extends Entity
return $this;
}
/**
* Возвращает установленную сумму акциза
*
* @return float|null
*/
public function getExcise(): ?float
{
return $this->excise;
}
/**
* Устанавливает сумму акциза
*
* @param float|null $excise
* @return Item
* @throws NegativeItemExciseException
*/
public function setExcise(?float $excise): Item
{
if ($excise < 0) {
throw new NegativeItemExciseException($this->getName(), $excise);
}
$this->excise = $excise;
return $this;
}
/**
* Возвращает установленный код страны происхождения товара
*
@ -499,16 +535,16 @@ class Item extends Entity
'quantity' => $this->getQuantity(),
'sum' => $this->getSum(),
];
$this->getMeasurementUnit() && $json['measurement_unit'] = $this->getMeasurementUnit();
$this->getPaymentMethod() && $json['payment_method'] = $this->getPaymentMethod();
$this->getPaymentObject() && $json['payment_object'] = $this->getPaymentObject();
$this->getDeclarationNumber() && $json['declaration_number'] = $this->getDeclarationNumber();
!is_null($this->getMeasurementUnit()) && $json['measurement_unit'] = $this->getMeasurementUnit();
!is_null($this->getPaymentMethod()) && $json['payment_method'] = $this->getPaymentMethod();
!is_null($this->getPaymentObject()) && $json['payment_object'] = $this->getPaymentObject();
!is_null($this->getDeclarationNumber()) && $json['declaration_number'] = $this->getDeclarationNumber();
$this->getVat()?->jsonSerialize() && $json['vat'] = $this->getVat()->jsonSerialize();
$this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo()->jsonSerialize();
$this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier()->jsonSerialize();
$this->getUserData() && $json['user_data'] = $this->getUserData();
//TODO excise
$this->getCountryCode() && $json['country_code'] = $this->getCountryCode();
!is_null($this->getUserData()) && $json['user_data'] = $this->getUserData();
!is_null($this->getExcise()) && $json['excise'] = $this->getExcise();
!is_null($this->getCountryCode()) && $json['country_code'] = $this->getCountryCode();
//TODO nomenclature_code
return $json;
}

View File

@ -0,0 +1,33 @@
<?php
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
declare(strict_types = 1);
namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при попытке указать предмету расчёта отрицательный акциз
*/
class NegativeItemExciseException extends AtolException
{
protected array $ffd_tags = [Ffd105Tags::ITEM_EXCISE];
/**
* Конструктор
*
* @param string $name
* @param float $excise
*/
public function __construct(string $name, float $excise)
{
parent::__construct("Предмет расчёта '$name' не может иметь отрицательный акциз $excise");
}
}

View File

@ -28,6 +28,7 @@ use AtolOnline\{
Exceptions\InvalidInnLengthException,
Exceptions\InvalidOKSMCodeException,
Exceptions\InvalidPhoneException,
Exceptions\NegativeItemExciseException,
Exceptions\NegativeItemPriceException,
Exceptions\NegativeItemQuantityException,
Exceptions\TooHighItemQuantityException,
@ -390,15 +391,16 @@ class ItemTest extends BasicTestCase
*
* @param mixed $vat
* @dataProvider providerNullableStrings
* @covers \AtolOnline\Entities\Item::setVat
* @covers \AtolOnline\Entities\Item::getVat
* @covers \AtolOnline\Entities\Item::jsonSerialize
* @covers \AtolOnline\Entities\Item::setVat
* @covers \AtolOnline\Entities\Item::getVat
* @covers \AtolOnline\Entities\Item::jsonSerialize
* @throws EmptyItemNameException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws TooHighPriceException
* @throws TooLongItemNameException
* @throws TooManyException
* @throws InvalidEnumValueException
*/
public function testNullableVatByString(mixed $vat): void
{
@ -656,4 +658,52 @@ class ItemTest extends BasicTestCase
(new Item('test item', 2, 3))
->setDeclarationNumber(Helpers::randomStr(Constraints::MAX_LENGTH_DECLARATION_NUMBER + 1));
}
/**
* Тестирует установку акциза и расчёт суммы с его учётом
*
* @covers \AtolOnline\Entities\Item::setExcise
* @covers \AtolOnline\Entities\Item::getExcise
* @covers \AtolOnline\Entities\Item::getSum
* @covers \AtolOnline\Entities\Item::jsonSerialize
* @throws TooLongItemNameException
* @throws TooHighPriceException
* @throws TooManyException
* @throws NegativeItemPriceException
* @throws EmptyItemNameException
* @throws NegativeItemQuantityException
* @throws NegativeItemExciseException
*/
public function testExcise(): void
{
$this->assertAtolable(
(new Item('test item', 2, 3))->setExcise(1),
[
'name' => 'test item',
'price' => 2,
'quantity' => 3,
'sum' => 7,
'excise' => 1,
]
);
}
/**
* Тестирует установку акциза и расчёт суммы с его учётом
*
* @covers \AtolOnline\Entities\Item::setExcise
* @covers \AtolOnline\Exceptions\NegativeItemExciseException
* @throws TooLongItemNameException
* @throws TooHighPriceException
* @throws TooManyException
* @throws NegativeItemPriceException
* @throws EmptyItemNameException
* @throws NegativeItemQuantityException
* @throws NegativeItemExciseException
*/
public function testNegativeItemExciseException(): void
{
$this->expectException(NegativeItemExciseException::class);
(new Item('test item', 2, 3))->setExcise(-1);
}
}