Третья итерация `Receipt`

- просчёт ставок секи в `setVats()`
- просчёт суммы чека в `setItems()`
- геттеры `getItems()` и `getVats()` возвращают пустую коллекцию, если в чеке они отсутствуют
- фикс `vats => supplier_info` в `jsonSerialize()`
- тесты поставщика, ставок, расчёта ставок и суммы чека
pull/15/head
Anthony Axenov 2021-12-08 16:01:25 +08:00
parent 793549aaac
commit fdc64954f9
2 changed files with 153 additions and 40 deletions

View File

@ -196,7 +196,7 @@ class Receipt extends Entity
*/ */
public function getItems(): Items public function getItems(): Items
{ {
return $this->items; return $this->items ?? new Items();
} }
/** /**
@ -207,6 +207,7 @@ class Receipt extends Entity
* @return Receipt * @return Receipt
* @throws EmptyItemsException * @throws EmptyItemsException
* @throws InvalidEntityInCollectionException * @throws InvalidEntityInCollectionException
* @throws Exception
*/ */
public function setItems(Items $items): self public function setItems(Items $items): self
{ {
@ -215,6 +216,8 @@ class Receipt extends Entity
} }
$items->checkItemsClasses(); $items->checkItemsClasses();
$this->items = $items; $this->items = $items;
$this->getItems()->each(fn ($item) => $this->total += $item->getSum());
$this->total = round($this->total, 2);
return $this; return $this;
} }
@ -251,7 +254,7 @@ class Receipt extends Entity
*/ */
public function getVats(): ?Vats public function getVats(): ?Vats
{ {
return $this->vats; return $this->vats ?? new Vats();
} }
/** /**
@ -260,6 +263,7 @@ class Receipt extends Entity
* @param Vats|null $vats * @param Vats|null $vats
* @return Receipt * @return Receipt
* @throws EmptyVatsException * @throws EmptyVatsException
* @throws Exception
*/ */
public function setVats(?Vats $vats): self public function setVats(?Vats $vats): self
{ {
@ -267,6 +271,8 @@ class Receipt extends Entity
throw new EmptyVatsException(); throw new EmptyVatsException();
} }
$this->vats = $vats; $this->vats = $vats;
/** @var Vat $vat */
$this->getVats()->each(fn ($vat) => $vat->setSum($this->getTotal()));
return $this; return $this;
} }
@ -280,18 +286,6 @@ class Receipt extends Entity
return $this->total; return $this->total;
} }
/**
* Устанавливает полную сумму чека
*
* @param float $total
* @return Receipt
*/
public function setTotal(float $total): self
{
$this->total = $total;
return $this;
}
/** /**
* Возвращает установленного кассира * Возвращает установленного кассира
* *
@ -387,28 +381,11 @@ class Receipt extends Entity
'payments' => $this->getPayments(), 'payments' => $this->getPayments(),
]; ];
$this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo(); $this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo();
$this->getSupplier()?->jsonSerialize() && $json['vats'] = $this->getVats(); $this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier();
$this->getVats()?->jsonSerialize() && $json['vats'] = $this->getVats(); $this->getVats()?->jsonSerialize() && $json['vats'] = $this->getVats();
!is_null($this->getAddCheckProps()) && $json['additional_check_props'] = $this->getAddCheckProps(); !is_null($this->getAddCheckProps()) && $json['additional_check_props'] = $this->getAddCheckProps();
!is_null($this->getCashier()) && $json['cashier'] = $this->getCashier(); !is_null($this->getCashier()) && $json['cashier'] = $this->getCashier();
$this->getAddUserProps()?->jsonSerialize() && $json['additional_user_props'] = $this->getAddUserProps(); $this->getAddUserProps()?->jsonSerialize() && $json['additional_user_props'] = $this->getAddUserProps();
return $json; return $json;
} }
///**
// * Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020.
// *
// * @return float
// * @throws Exception
// */
//public function calcTotal(): float
//{
// $sum = 0;
// $this->clearVats();
// foreach ($this->items->get() as $item) {
// $sum += $item->calcSum();
// $this->addVat(new Vat($item->getVat()->getType(), $item->getSum()));
// }
// return $this->total = round($sum, 2);
//}
} }

View File

@ -12,6 +12,7 @@ namespace AtolOnline\Tests\Entities;
use AtolOnline\{ use AtolOnline\{
Collections\Items, Collections\Items,
Collections\Payments, Collections\Payments,
Collections\Vats,
Entities\AgentInfo, Entities\AgentInfo,
Entities\Client, Entities\Client,
Entities\Company, Entities\Company,
@ -20,6 +21,8 @@ use AtolOnline\{
Entities\PayingAgent, Entities\PayingAgent,
Entities\Receipt, Entities\Receipt,
Entities\ReceivePaymentsOperator, Entities\ReceivePaymentsOperator,
Entities\Supplier,
Entities\Vat,
Enums\AgentTypes, Enums\AgentTypes,
Enums\SnoTypes, Enums\SnoTypes,
Tests\BasicTestCase}; Tests\BasicTestCase};
@ -27,6 +30,7 @@ use AtolOnline\Exceptions\{
EmptyItemNameException, EmptyItemNameException,
EmptyItemsException, EmptyItemsException,
EmptyPaymentsException, EmptyPaymentsException,
EmptyVatsException,
InvalidEntityInCollectionException, InvalidEntityInCollectionException,
InvalidEnumValueException, InvalidEnumValueException,
InvalidInnLengthException, InvalidInnLengthException,
@ -35,6 +39,7 @@ use AtolOnline\Exceptions\{
NegativeItemQuantityException, NegativeItemQuantityException,
NegativePaymentSumException, NegativePaymentSumException,
TooHighItemPriceException, TooHighItemPriceException,
TooHighItemSumException,
TooHighPaymentSumException, TooHighPaymentSumException,
TooLongItemNameException, TooLongItemNameException,
TooLongPayingAgentOperationException, TooLongPayingAgentOperationException,
@ -76,8 +81,9 @@ class ReceiptTest extends BasicTestCase
*/ */
public function testConstructor(): void public function testConstructor(): void
{ {
$receipt = $this->validReceipt(); $receipt = $this->newReceipt();
$this->assertIsAtolable($receipt); $this->assertIsAtolable($receipt);
$receipt->getItems();
} }
/** /**
@ -104,7 +110,7 @@ class ReceiptTest extends BasicTestCase
* @throws TooLongPayingAgentOperationException * @throws TooLongPayingAgentOperationException
* @throws Exception * @throws Exception
*/ */
public function testSetAgentInfo(): void public function testAgentInfo(): void
{ {
$agent_info = new AgentInfo( $agent_info = new AgentInfo(
AgentTypes::ANOTHER, AgentTypes::ANOTHER,
@ -112,9 +118,42 @@ class ReceiptTest extends BasicTestCase
new ReceivePaymentsOperator(['+79519999999']), new ReceivePaymentsOperator(['+79519999999']),
new MoneyTransferOperator('MTO Name', '9876543210', 'London', ['+79517777777']), new MoneyTransferOperator('MTO Name', '9876543210', 'London', ['+79517777777']),
); );
$receipt = $this->validReceipt()->setAgentInfo($agent_info); $receipt = $this->newReceipt()->setAgentInfo($agent_info);
$this->assertArrayHasKey('agent_info', $receipt->jsonSerialize()); $this->assertArrayHasKey('agent_info', $receipt->jsonSerialize());
$this->assertEquals($receipt->getAgentInfo(), $receipt->jsonSerialize()['agent_info']); $this->assertEquals($receipt->getAgentInfo(), $receipt->jsonSerialize()['agent_info']);
$this->assertArrayNotHasKey('agent_info', $receipt->setAgentInfo(null)->jsonSerialize());
}
/**
* Тестирует установку данных поставщика
*
* @return void
* @covers \AtolOnline\Entities\Receipt::setSupplier
* @covers \AtolOnline\Entities\Receipt::getSupplier
* @covers \AtolOnline\Entities\Receipt::jsonSerialize
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyPaymentsException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPhoneException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooManyException
* @throws Exception
*/
public function testSupplier(): void
{
$supplier = new Supplier('some name', '+fasd3\qe3fs_=nac99013928czc', ['+122997365456']);
$receipt = $this->newReceipt()->setSupplier($supplier);
$this->assertArrayHasKey('supplier_info', $receipt->jsonSerialize());
$this->assertEquals($receipt->getSupplier(), $receipt->jsonSerialize()['supplier_info']);
$this->assertArrayNotHasKey('supplier_info', $receipt->setSupplier(null)->jsonSerialize());
} }
/** /**
@ -122,7 +161,7 @@ class ReceiptTest extends BasicTestCase
* *
* @return void * @return void
* @covers \AtolOnline\Entities\Receipt * @covers \AtolOnline\Entities\Receipt
* @covers \AtolOnline\Entities\Receipt::setVats * @covers \AtolOnline\Entities\Receipt::setItems
* @covers \AtolOnline\Collections\Items::checkCount * @covers \AtolOnline\Collections\Items::checkCount
* @covers \AtolOnline\Exceptions\EmptyItemsException * @covers \AtolOnline\Exceptions\EmptyItemsException
* @throws InvalidEnumValueException * @throws InvalidEnumValueException
@ -147,7 +186,7 @@ class ReceiptTest extends BasicTestCase
* *
* @return void * @return void
* @covers \AtolOnline\Entities\Receipt * @covers \AtolOnline\Entities\Receipt
* @covers \AtolOnline\Entities\Receipt::setVats * @covers \AtolOnline\Entities\Receipt::setItems
* @covers \AtolOnline\Collections\Items::checkItemsClasses * @covers \AtolOnline\Collections\Items::checkItemsClasses
* @covers \AtolOnline\Collections\Items::checkItemClass * @covers \AtolOnline\Collections\Items::checkItemClass
* @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException
@ -204,7 +243,7 @@ class ReceiptTest extends BasicTestCase
* *
* @return void * @return void
* @covers \AtolOnline\Entities\Receipt * @covers \AtolOnline\Entities\Receipt
* @covers \AtolOnline\Entities\Receipt::setVats * @covers \AtolOnline\Entities\Receipt::setPayments
* @covers \AtolOnline\Collections\Items::checkItemsClasses * @covers \AtolOnline\Collections\Items::checkItemsClasses
* @covers \AtolOnline\Collections\Items::checkItemClass * @covers \AtolOnline\Collections\Items::checkItemClass
* @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException
@ -230,6 +269,103 @@ class ReceiptTest extends BasicTestCase
); );
} }
/**
* Тестирует выброс исключения при передаче пустой коллекции ставок НДС
*
* @return void
* @covers \AtolOnline\Entities\Receipt
* @covers \AtolOnline\Entities\Receipt::setVats
* @covers \AtolOnline\Collections\Vats::checkCount
* @covers \AtolOnline\Exceptions\EmptyVatsException
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyPaymentsException
* @throws EmptyVatsException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooManyException
*/
public function testEmptyVatsException(): void
{
$this->expectException(EmptyVatsException::class);
$this->newReceipt()->setVats(new Vats([]));
}
/**
* Тестирует выброс исключения при передаче коллекции ставок НДС с некорректным содержимым
*
* @return void
* @covers \AtolOnline\Entities\Receipt
* @covers \AtolOnline\Entities\Receipt::setVats
* @covers \AtolOnline\Collections\Vats::checkItemsClasses
* @covers \AtolOnline\Collections\Vats::checkItemClass
* @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyPaymentsException
* @throws EmptyVatsException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooManyException
*/
public function testInvalidVatInCollectionException(): void
{
$this->expectException(InvalidEntityInCollectionException::class);
$this->expectErrorMessage('Коллекция AtolOnline\Collections\Vats должна содержать объекты AtolOnline\Entities\Vat');
$this->newReceipt()->setVats(new Vats(['qwerty']));
}
/**
* Тестирует просчёт общей суммы чека и ставок НДС
*
* @covers \AtolOnline\Entities\Receipt::setVats
* @covers \AtolOnline\Entities\Receipt::getVats
* @covers \AtolOnline\Entities\Receipt::getTotal
* @throws TooHighItemPriceException
* @throws NegativeItemPriceException
* @throws EmptyPaymentsException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws TooHighItemSumException
* @throws NegativePaymentSumException
* @throws TooHighPaymentSumException
* @throws EmptyItemsException
* @throws EmptyItemNameException
* @throws TooManyException
* @throws NegativeItemQuantityException
* @throws TooLongItemNameException
* @throws Exception
*/
public function testCalculations(): void
{
$items_total = 0;
$receipt = $this->newReceipt();
//TODO при $receipt->getItems()->pluck('sum') стреляет InvalidEntityInCollectionException
// см. примечания в конструкторе EntityCollection
$receipt->getItems()->each(function ($item) use (&$items_total) {
/** @var Item $item */
return $items_total += $item->getSum();
});
$this->assertEquals($items_total, $receipt->getTotal());
/** @var Vat $vat */
$receipt->setVats(new Vats($this->generateVatObjects(2)))->getVats()
->each(fn ($vat) => $this->assertEquals($items_total, $vat->getSum()));
}
/** /**
* Возвращает валидный тестовый объект чека * Возвращает валидный тестовый объект чека
* *
@ -247,12 +383,12 @@ class ReceiptTest extends BasicTestCase
* @throws TooLongItemNameException * @throws TooLongItemNameException
* @throws TooManyException * @throws TooManyException
*/ */
protected function validReceipt(): Receipt protected function newReceipt(): Receipt
{ {
return new Receipt( return new Receipt(
new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'),
new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'),
new Items($this->generateItemObjects()), new Items($this->generateItemObjects(2)),
new Payments($this->generatePaymentObjects()) new Payments($this->generatePaymentObjects())
); );
} }