Compare commits

...

3 Commits

Author SHA1 Message Date
267431ec28 Мелочи в readme и phpunit.xml (удалены дефолтные параметры) 2021-12-02 01:10:54 +08:00
cb24bb1fb0 Доработки енамов и тегов ФФД 2021-12-02 01:10:16 +08:00
11646113b6 Доработки Item
- поддержка `excise`, покрыта тестами
- фикс `setVat()`
- улучшен `jsonSerialize()`
2021-12-02 01:09:25 +08:00
14 changed files with 259 additions and 38 deletions

View File

@ -84,9 +84,9 @@ composer test
## Дополнительные ресурсы ## Дополнительные ресурсы
* [Документация АТОЛ](https://online.atol.ru/lib/) * **[Документация к библиотеке](/docs/readme.md)**
**[Документация к библиотеке](/docs/readme.md)**
* Telegram-канал: [@atolonline_php](https://t.me/atolonline_php) * Telegram-канал: [@atolonline_php](https://t.me/atolonline_php)
* [Документация АТОЛ Онлайн](https://online.atol.ru/lib/)
* Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md) * Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md)
## Лицензия ## Лицензия

View File

@ -9,15 +9,8 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd" xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php" bootstrap="vendor/autoload.php"
colors="true" colors="true">
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites> <testsuites>
<testsuite name="All"> <testsuite name="All">
<directory suffix="Test.php">./tests</directory> <directory suffix="Test.php">./tests</directory>

View File

@ -39,11 +39,21 @@ final class Ffd105Tags
*/ */
const COMPANY_INN = 1008; const COMPANY_INN = 1008;
/**
* Применяемая система налогообложения
*/
const COMPANY_SNO = 1055;
/** /**
* Место расчётов * Место расчётов
*/ */
const COMPANY_PADDRESS = 1187; const COMPANY_PADDRESS = 1187;
/**
* Признак агента по предмету расчёта
*/
const AGENT_TYPE = 1222;
/** /**
* Телефон оператора по приёму платежей * Телефон оператора по приёму платежей
*/ */
@ -132,7 +142,7 @@ final class Ffd105Tags
/** /**
* Сумма акциза с учётом копеек, включённая в стоимость предмета расчёта * Сумма акциза с учётом копеек, включённая в стоимость предмета расчёта
*/ */
const ITEM_PAYMENT_EXSICE = 1229; const ITEM_EXCISE = 1229;
/** /**
* Цифровой код страны происхождения товара в соответствии с Общероссийским классификатором стран мира * Цифровой код страны происхождения товара в соответствии с Общероссийским классификатором стран мира
@ -146,4 +156,69 @@ final class Ffd105Tags
* Номер таможенной декларации (в соотв. с приказом ФНС России от 24.03.2016 N ММВ-7-15/155) * Номер таможенной декларации (в соотв. с приказом ФНС России от 24.03.2016 N ММВ-7-15/155)
*/ */
const ITEM_DECLARATION_NUMBER = 1231; const ITEM_DECLARATION_NUMBER = 1231;
/**
* Тип коррекции
*/
const CORRECTION_TYPE = 1173;
/**
* Сумма по чеку (БСО) наличными
*/
const PAYMENT_TYPE_CASH = 1031;
/**
* Сумма по чеку безналичными
*/
const PAYMENT_TYPE_ELECTRON = 1081;
/**
* Сумма по чеку предоплатой
*/
const PAYMENT_TYPE_PREPAID = 1215;
/**
* Сумма по чеку постоплатой
*/
const PAYMENT_TYPE_CREDIT = 1216;
/**
* Сумма по чеку встречным представлением
*/
const PAYMENT_TYPE_OTHER = 1217;
/**
* Ставка НДС
*/
const ITEM_VAT_TYPE = 1199;
/**
* Сумма расчета по чеку без НДС
*/
const DOC_VAT_TYPE_NONE = 1105;
/**
* Сумма расчета по чеку с НДС по ставке 0%
*/
const DOC_VAT_TYPE_VAT0 = 1104;
/**
* Сумма НДС чека по ставке 10%
*/
const DOC_VAT_TYPE_VAT10 = 1103;
/**
* Сумма НДС чека по ставке 20%
*/
const DOC_VAT_TYPE_VAT20 = 1102;
/**
* Сумма НДС чека по расч. ставке 10/110
*/
const DOC_VAT_TYPE_VAT110 = 1107;
/**
* Сумма НДС чека по расч. ставке 20/120
*/
const DOC_VAT_TYPE_VAT120 = 1106;
} }

View File

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

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Entities; namespace AtolOnline\Entities;
use AtolOnline\Enums\VatTypes; use AtolOnline\Enums\VatTypes;
use AtolOnline\Exceptions\InvalidEnumValueException;
use AtolOnline\Helpers; use AtolOnline\Helpers;
/** /**
@ -48,6 +49,7 @@ class Vat extends Entity
* *
* @param string $type Тип ставки НДС * @param string $type Тип ставки НДС
* @return $this * @return $this
* @throws InvalidEnumValueException
*/ */
public function setType(string $type): self public function setType(string $type): self
{ {

View File

@ -11,10 +11,12 @@ declare(strict_types = 1);
namespace AtolOnline\Enums; namespace AtolOnline\Enums;
use AtolOnline\Constants\Ffd105Tags;
/** /**
* Константы, определяющие типы агента * Константы, определяющие типы агента
* *
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 18 * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 18, 26
*/ */
final class AgentTypes extends Enum final class AgentTypes extends Enum
{ {
@ -58,6 +60,6 @@ final class AgentTypes extends Enum
*/ */
public static function getFfdTags(): array public static function getFfdTags(): array
{ {
return [1057]; return [Ffd105Tags::AGENT_TYPE];
} }
} }

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Enums; namespace AtolOnline\Enums;
use AtolOnline\Constants\Ffd105Tags;
/** /**
* Константы, определяющие типы документов коррекции * Константы, определяющие типы документов коррекции
* *
@ -33,6 +35,6 @@ final class CorrectionTypes extends Enum
*/ */
public static function getFfdTags(): array public static function getFfdTags(): array
{ {
return [1173]; return [Ffd105Tags::CORRECTION_TYPE];
} }
} }

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Enums; namespace AtolOnline\Enums;
use AtolOnline\Constants\Ffd105Tags;
/** /**
* Константы, определяющие признаки способов расчёта * Константы, определяющие признаки способов расчёта
* *
@ -58,6 +60,6 @@ final class PaymentMethods extends Enum
*/ */
public static function getFfdTags(): array public static function getFfdTags(): array
{ {
return [1214]; return [Ffd105Tags::ITEM_PAYMENT_METHOD];
} }
} }

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Enums; namespace AtolOnline\Enums;
use AtolOnline\Constants\Ffd105Tags;
/** /**
* Константы, определяющие признаки предметов расчёта * Константы, определяющие признаки предметов расчёта
* *
@ -160,6 +162,6 @@ final class PaymentObjects extends Enum
*/ */
public static function getFfdTags(): array public static function getFfdTags(): array
{ {
return [1212]; return [Ffd105Tags::ITEM_PAYMENT_OBJECT];
} }
} }

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Enums; namespace AtolOnline\Enums;
use AtolOnline\Constants\Ffd105Tags;
/** /**
* Константы, определяющие виды оплат * Константы, определяющие виды оплат
* *
@ -31,7 +33,7 @@ final class PaymentTypes extends Enum
/** /**
* Предварительная оплата (зачёт аванса) * Предварительная оплата (зачёт аванса)
*/ */
const PRE_PAID = 2; const PREPAID = 2;
/** /**
* Предварительная оплата (кредит) * Предварительная оплата (кредит)
@ -78,6 +80,12 @@ final class PaymentTypes extends Enum
*/ */
public static function getFfdTags(): array public static function getFfdTags(): array
{ {
return [1031, 1081, 1215, 1216, 1217]; return [
Ffd105Tags::PAYMENT_TYPE_CASH,
Ffd105Tags::PAYMENT_TYPE_ELECTRON,
Ffd105Tags::PAYMENT_TYPE_PREPAID,
Ffd105Tags::PAYMENT_TYPE_CREDIT,
Ffd105Tags::PAYMENT_TYPE_OTHER,
];
} }
} }

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Enums; namespace AtolOnline\Enums;
use AtolOnline\Constants\Ffd105Tags;
/** /**
* Константы, определяющие типы налогообложения * Константы, определяющие типы налогообложения
* *
@ -53,6 +55,6 @@ final class SnoTypes extends Enum
*/ */
public static function getFfdTags(): array public static function getFfdTags(): array
{ {
return [1055]; return [Ffd105Tags::COMPANY_SNO];
} }
} }

View File

@ -11,12 +11,10 @@ declare(strict_types = 1);
namespace AtolOnline\Enums; namespace AtolOnline\Enums;
use MyCLabs\Enum\Enum; use AtolOnline\Constants\Ffd105Tags;
/** /**
* Константы, определяющие типы ставок НДС * Константы, определяющие типы ставок НДС
*
* Теги ФФД: 1199, 1105, 1104, 1103, 1102, 1107, 1106
*/ */
final class VatTypes extends Enum final class VatTypes extends Enum
{ {
@ -59,4 +57,20 @@ final class VatTypes extends Enum
* НДС 20/120% * НДС 20/120%
*/ */
const VAT120 = 'vat120'; const VAT120 = 'vat120';
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [
Ffd105Tags::ITEM_VAT_TYPE,
Ffd105Tags::DOC_VAT_TYPE_NONE,
Ffd105Tags::DOC_VAT_TYPE_VAT0,
Ffd105Tags::DOC_VAT_TYPE_VAT10,
Ffd105Tags::DOC_VAT_TYPE_VAT20,
Ffd105Tags::DOC_VAT_TYPE_VAT110,
Ffd105Tags::DOC_VAT_TYPE_VAT120,
];
}
} }

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\InvalidInnLengthException,
Exceptions\InvalidOKSMCodeException, Exceptions\InvalidOKSMCodeException,
Exceptions\InvalidPhoneException, Exceptions\InvalidPhoneException,
Exceptions\NegativeItemExciseException,
Exceptions\NegativeItemPriceException, Exceptions\NegativeItemPriceException,
Exceptions\NegativeItemQuantityException, Exceptions\NegativeItemQuantityException,
Exceptions\TooHighItemQuantityException, Exceptions\TooHighItemQuantityException,
@ -399,6 +400,7 @@ class ItemTest extends BasicTestCase
* @throws TooHighPriceException * @throws TooHighPriceException
* @throws TooLongItemNameException * @throws TooLongItemNameException
* @throws TooManyException * @throws TooManyException
* @throws InvalidEnumValueException
*/ */
public function testNullableVatByString(mixed $vat): void public function testNullableVatByString(mixed $vat): void
{ {
@ -656,4 +658,52 @@ class ItemTest extends BasicTestCase
(new Item('test item', 2, 3)) (new Item('test item', 2, 3))
->setDeclarationNumber(Helpers::randomStr(Constraints::MAX_LENGTH_DECLARATION_NUMBER + 1)); ->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);
}
} }