diff --git a/UPGRADE.md b/UPGRADE.md index 1af394511..719646955 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,12 +1,12 @@ ### UPGRADE FROM 1.0.0-RC.1 TO 1.0.0-RC.2 -1. `CreditMemoUnit` changed to `LineItem` which is a resource entity now +1. `Sylius\RefundPlugin\Entity\CreditMemoUnit` was changed to `Sylius\RefundPlugin\Entity\LineItem` which is a resource entity now. -2. `CreditMemoUnitGeneratorInterface` changed to `LineItemsConverterInterface` and moved from `Generator` to `Converter` directory +2. `Sylius\RefundPlugin\Generator\CreditMemoUnitGeneratorInterface` was changed to `Sylius\RefundPlugin\Converter\LineItemsConverterInterface`. -3. `OrderItemUnitCreditMemoUnitGenerator` changed to `LineItemsConverter` and moved from `Generator` to `Converter` directory +3. `Sylius\RefundPlugin\Generator\OrderItemUnitCreditMemoUnitGenerator` was changed to `Sylius\RefundPlugin\Converter\LineItemsConverter`. -4. `ShipmentCreditMemoUnitGenerator` changed to `ShipmentLineItemsConverter` and moved from `Generator` to `Converter` directory +4. `Sylius\RefundPlugin\Generator\ShipmentCreditMemoUnitGenerator` was changed to `Sylius\RefundPlugin\Converter\ShipmentLineItemsConverter`. There are no migrations that provide backward compatibility, save current credit memos before upgrading the version of plugin. diff --git a/features/refunding_part_of_order_unit.feature b/features/refunding_part_of_order_unit.feature index 67f28d1d1..bae80bb1f 100644 --- a/features/refunding_part_of_order_unit.feature +++ b/features/refunding_part_of_order_unit.feature @@ -28,7 +28,7 @@ Feature: Refunding a part of an order unit @ui @application Scenario: Refunding the whole order unit price after partial refund - Given the 1st "Mr. Meeseeks T-Shirt" product from order "#00000022" already has a refund of "$5.00" with "Space money" payment + Given the 1st "Mr. Meeseeks T-Shirt" product from order "#00000022" has a refund of "$5.00" with "Space money" payment When I want to refund some units of order "#00000022" And I decide to refund 1st "Mr. Meeseeks T-Shirt" product with "Space money" payment Then I should be notified that selected order units have been successfully refunded @@ -36,7 +36,7 @@ Feature: Refunding a part of an order unit @ui @javascript Scenario: Refunding the whole order price after partial refund - Given the 1st "Mr. Meeseeks T-Shirt" product from order "#00000022" already has a refund of "$5.00" with "Space money" payment + Given the 1st "Mr. Meeseeks T-Shirt" product from order "#00000022" has a refund of "$5.00" with "Space money" payment When I want to refund some units of order "#00000022" And I decide to refund all units of this order with "Space money" payment Then I should be notified that selected order units have been successfully refunded @@ -44,7 +44,7 @@ Feature: Refunding a part of an order unit @ui @application Scenario: Not being able to refund more money than order unit total - Given the 1st "Mr. Meeseeks T-Shirt" product from order "#00000022" already has a refund of "$5.00" with "Space money" payment + Given the 1st "Mr. Meeseeks T-Shirt" product from order "#00000022" has a refund of "$5.00" with "Space money" payment When I want to refund some units of order "#00000022" And I decide to refund "$10.00" from 1st "Mr. Meeseeks T-Shirt" product with "Space money" payment Then I should be notified that I cannot refund more money than the order unit total diff --git a/features/viewing_details_of_credit_memo.feature b/features/viewing_details_of_credit_memo.feature index 4e20be55a..88d61772a 100644 --- a/features/viewing_details_of_credit_memo.feature +++ b/features/viewing_details_of_credit_memo.feature @@ -39,7 +39,7 @@ Feature: Viewing details of a credit memo @ui @application Scenario: Viewing details of a credit memo issued for a partial refund - Given the 1st "PHP T-Shirt" product from order "#00000022" already has a refund of "$5.50" with "Space money" payment + Given the 1st "PHP T-Shirt" product from order "#00000022" has a refund of "$5.50" with "Space money" payment When I browse the details of the only credit memo generated for order "#00000022" Then it should have sequential number generated from current date And it should be issued in "United States" channel diff --git a/spec/Entity/LineItemSpec.php b/spec/Entity/LineItemSpec.php index 59aa0a2bf..ea80c17e9 100644 --- a/spec/Entity/LineItemSpec.php +++ b/spec/Entity/LineItemSpec.php @@ -7,6 +7,7 @@ use PhpSpec\ObjectBehavior; use Sylius\Component\Resource\Model\ResourceInterface; use Sylius\RefundPlugin\Entity\LineItemInterface; +use Sylius\RefundPlugin\Exception\LineItemsCannotBeMerged; final class LineItemSpec extends ObjectBehavior { @@ -36,4 +37,49 @@ function it_has_proper_line_item_data(): void $this->taxAmount()->shouldReturn(200); $this->taxRate()->shouldReturn('10%'); } + + function it_merges_with_another_line_item(LineItemInterface $newLineItem): void + { + $newLineItem->name()->willReturn('Mjolnir'); + $newLineItem->quantity()->willReturn(1); + $newLineItem->unitNetPrice()->willReturn(1000); + $newLineItem->unitGrossPrice()->willReturn(1100); + $newLineItem->netValue()->willReturn(1000); + $newLineItem->grossValue()->willReturn(1100); + $newLineItem->taxAmount()->willReturn(100); + $newLineItem->taxRate()->willReturn('10%'); + + $this->merge($newLineItem); + + $this->quantity()->shouldReturn(3); + $this->netValue()->shouldReturn(3000); + $this->grossValue()->shouldReturn(3300); + $this->taxAmount()->shouldReturn(300); + } + + function it_throws_an_exception_if_another_line_item_is_different_during_merging(LineItemInterface $newLineItem): void + { + $newLineItem->name()->willReturn('Stormbreaker'); + $newLineItem->unitNetPrice()->willReturn(1000); + $newLineItem->unitGrossPrice()->willReturn(1100); + $newLineItem->taxRate()->willReturn('10%'); + + $this->shouldThrow(LineItemsCannotBeMerged::class)->during('merge', [$newLineItem]); + } + + function it_compares_with_another_line_item(LineItemInterface $theSameLineItem, LineItemInterface $differentLineItem): void + { + $theSameLineItem->name()->willReturn('Mjolnir'); + $theSameLineItem->unitNetPrice()->willReturn(1000); + $theSameLineItem->unitGrossPrice()->willReturn(1100); + $theSameLineItem->taxRate()->willReturn('10%'); + + $differentLineItem->name()->willReturn('Stormbreaker'); + $differentLineItem->unitNetPrice()->willReturn(1000); + $differentLineItem->unitGrossPrice()->willReturn(1100); + $differentLineItem->taxRate()->willReturn('10%'); + + $this->compare($theSameLineItem)->shouldReturn(true); + $this->compare($differentLineItem)->shouldReturn(false); + } } diff --git a/src/Converter/LineItemsConverter.php b/src/Converter/LineItemsConverter.php index 38f14b561..f8d70250f 100644 --- a/src/Converter/LineItemsConverter.php +++ b/src/Converter/LineItemsConverter.php @@ -6,6 +6,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; +use function Sodium\compare; use Sylius\Component\Core\Model\AdjustmentInterface; use Sylius\Component\Core\Model\OrderItemInterface; use Sylius\Component\Core\Model\OrderItemUnitInterface; @@ -31,7 +32,7 @@ public function convert(array $units): Collection /** @var UnitRefundInterface $unitRefund */ foreach ($units as $unitRefund) { - /** @var OrderItemUnitInterface $orderItemUnit */ + /** @var OrderItemUnitInterface|null $orderItemUnit */ $orderItemUnit = $this->orderItemUnitRepository->find($unitRefund->id()); Assert::notNull($orderItemUnit); Assert::lessThanEq($unitRefund->total(), $orderItemUnit->getTotal()); @@ -70,12 +71,7 @@ private function addLineItem(LineItemInterface $newLineItem, Collection $lineIte { /** @var LineItemInterface $lineItem */ foreach ($lineItems as $lineItem) { - if ( - $lineItem->name() === $newLineItem->name() && - $lineItem->unitNetPrice() === $newLineItem->unitNetPrice() && - $lineItem->unitGrossPrice() === $newLineItem->unitGrossPrice() && - $lineItem->taxRate() === $newLineItem->taxRate() - ) { + if ($lineItem->compare($newLineItem)) { $lineItem->merge($newLineItem); return $lineItems; @@ -98,7 +94,7 @@ private function getTaxRate(Collection $taxAdjustments): ?string $label = $taxAdjustments->first()->getLabel(); - if (preg_match('#\((.*?)\)#', $label, $matches)) { + if (preg_match('/\((.*?)\)/', $label, $matches)) { return end($matches); } diff --git a/src/Converter/LineItemsConverterInterface.php b/src/Converter/LineItemsConverterInterface.php index 5bc315231..398673a09 100644 --- a/src/Converter/LineItemsConverterInterface.php +++ b/src/Converter/LineItemsConverterInterface.php @@ -11,7 +11,7 @@ interface LineItemsConverterInterface { /** - * @param array $units + * @param UnitRefundInterface[] $units * * @return Collection|LineItemInterface[] */ diff --git a/src/Converter/ShipmentLineItemsConverter.php b/src/Converter/ShipmentLineItemsConverter.php index 3965d4b34..527a71f24 100644 --- a/src/Converter/ShipmentLineItemsConverter.php +++ b/src/Converter/ShipmentLineItemsConverter.php @@ -28,7 +28,7 @@ public function convert(array $units): Collection /** @var UnitRefundInterface $unitRefund */ foreach ($units as $unitRefund) { - /** @var AdjustmentInterface $shippingAdjustment */ + /** @var AdjustmentInterface|null $shippingAdjustment */ $shippingAdjustment = $this ->adjustmentRepository ->findOneBy(['id' => $unitRefund->id(), 'type' => AdjustmentInterface::SHIPPING_ADJUSTMENT]) diff --git a/src/Entity/LineItem.php b/src/Entity/LineItem.php index d6e1a24ea..4f53bcda2 100644 --- a/src/Entity/LineItem.php +++ b/src/Entity/LineItem.php @@ -4,6 +4,8 @@ namespace Sylius\RefundPlugin\Entity; +use Sylius\RefundPlugin\Exception\LineItemsCannotBeMerged; + /** @final */ class LineItem implements LineItemInterface { @@ -106,9 +108,27 @@ public function taxRate(): ?string public function merge(LineItemInterface $newLineItem): void { + if (!$this->compare($newLineItem)) { + throw LineItemsCannotBeMerged::occur(); + } + $this->quantity += $newLineItem->quantity(); $this->netValue += $newLineItem->netValue(); $this->grossValue += $newLineItem->grossValue(); $this->taxAmount += $newLineItem->taxAmount(); } + + public function compare(LineItemInterface $lineItem): bool + { + if ( + $this->name() === $lineItem->name() && + $this->unitNetPrice() === $lineItem->unitNetPrice() && + $this->unitGrossPrice() === $lineItem->unitGrossPrice() && + $this->taxRate() === $lineItem->taxRate() + ) { + return true; + } + + return false; + } } diff --git a/src/Entity/LineItemInterface.php b/src/Entity/LineItemInterface.php index 69d7d5781..1140b7956 100644 --- a/src/Entity/LineItemInterface.php +++ b/src/Entity/LineItemInterface.php @@ -26,5 +26,7 @@ public function taxAmount(): int; public function taxRate(): ?string; - public function merge(self $newLineItem): void; + public function merge(LineItemInterface $newLineItem): void; + + public function compare(LineItemInterface $lineItem): bool; } diff --git a/src/Exception/LineItemsCannotBeMerged.php b/src/Exception/LineItemsCannotBeMerged.php new file mode 100644 index 000000000..be43b5fdb --- /dev/null +++ b/src/Exception/LineItemsCannotBeMerged.php @@ -0,0 +1,13 @@ + - + diff --git a/src/Resources/views/Download/creditMemo.html.twig b/src/Resources/views/Download/creditMemo.html.twig index 84da41e93..841b2449e 100644 --- a/src/Resources/views/Download/creditMemo.html.twig +++ b/src/Resources/views/Download/creditMemo.html.twig @@ -17,6 +17,7 @@ .credit-memo table tr.item td{ border-bottom: 1px solid #eee; } .credit-memo table tr.item.last td { border-bottom: none; } .credit-memo table tr.total td { border-top: 2px solid #eee; font-weight: bold; } + .credit-memo table tr.number td { text-align: center; font-size: 20px; font-weight: bold; padding-bottom: 40px; } @@ -32,8 +33,7 @@ - Credit memo #{{ creditMemo.number }}
- Issued at: {{ creditMemo.issuedAt|date('Y-m-d H:i:s') }}
+ {{ 'sylius_refund.ui.issued_at'|trans }}: {{ creditMemo.issuedAt|date('Y-m-d H:i:s') }}
@@ -72,6 +72,10 @@ {% endif %} + + {{ 'sylius_refund.ui.credit_memo'|trans }} #{{ creditMemo.number }} + + {{ 'sylius_refund.ui.no'|trans }} {{ 'sylius.ui.name'|trans }} @@ -89,11 +93,11 @@ {{ loop.index }} {{ item.name }} {{ item.quantity }} - {{ (item.unitNetPrice/100)|number_format(2) }} - {{ (item.netValue/100)|number_format(2) }} + {{ '%0.2f'|format(item.unitNetPrice/100) }} + {{ '%0.2f'|format(item.netValue/100) }} {{ item.taxRate }} - {{ (item.taxAmount/100)|number_format(2) }} - {{ (item.grossValue/100)|number_format(2) }} + {{ '%0.2f'|format(item.taxAmount/100) }} + {{ '%0.2f'|format(item.grossValue/100) }} {{ creditMemo.currencyCode }} {% endfor %} @@ -101,7 +105,7 @@ {{ 'sylius.ui.total'|trans }}: - {{ (creditMemo.total/100)|number_format(2) }} + {{ '%0.2f'|format(creditMemo.total/100) }} {{ creditMemo.currencyCode }} @@ -117,7 +121,7 @@ {{ item.label }} - {{ (item.amount/100)|number_format(2) }} + {{ '%0.2f'|format(item.amount/100) }} {{ creditMemo.currencyCode }} {% endfor %} diff --git a/src/Resources/views/Order/Admin/CreditMemo/details.html.twig b/src/Resources/views/Order/Admin/CreditMemo/details.html.twig index 39a70e5ea..06757850f 100644 --- a/src/Resources/views/Order/Admin/CreditMemo/details.html.twig +++ b/src/Resources/views/Order/Admin/CreditMemo/details.html.twig @@ -92,19 +92,19 @@ {{ item.quantity }} - {{ (item.unitNetPrice/100)|number_format(2) }} + {{ '%0.2f'|format(item.unitNetPrice/100) }} - {{ (item.netValue/100)|number_format(2) }} + {{ '%0.2f'|format(item.netValue/100) }} {{ item.taxRate }} - {{ (item.taxAmount/100)|number_format(2) }} + {{ '%0.2f'|format(item.taxAmount/100) }} - {{ (item.grossValue/100)|number_format(2) }} + {{ '%0.2f'|format(item.grossValue/100) }} {{ credit_memo.currencyCode }} @@ -118,7 +118,7 @@ {{ 'sylius.ui.total'|trans }}: - {{ (credit_memo.total/100)|number_format(2) }} + {{ '%0.2f'|format(credit_memo.total/100) }} {{ credit_memo.currencyCode }} @@ -130,7 +130,7 @@ {{ item.label }}: - {{ (item.amount/100)|number_format(2) }} + {{ '%0.2f'|format(item.amount/100) }} {{ credit_memo.currencyCode }} diff --git a/tests/Behat/Context/Setup/RefundingContext.php b/tests/Behat/Context/Setup/RefundingContext.php index 431d13018..ab3e12bc1 100644 --- a/tests/Behat/Context/Setup/RefundingContext.php +++ b/tests/Behat/Context/Setup/RefundingContext.php @@ -58,9 +58,9 @@ public function productFromOrderHasAlreadyBeenRefunded( } /** - * @Given /^the (\d)(?:|st|nd|rd) "([^"]+)" product from order "#([^"]+)" already has a refund of ("[^"]+") with ("[^"]+" payment)$/ + * @Given /^the (\d)(?:|st|nd|rd) "([^"]+)" product from order "#([^"]+)" has a refund of ("[^"]+") with ("[^"]+" payment)$/ */ - public function partOfProductFromOrderHasAlreadyBeenRefunded( + public function theProductFromOrderHasARefundOfWith( int $unitNumber, string $productName, string $orderNumber, diff --git a/tests/Behat/Context/Transform/PriceContext.php b/tests/Behat/Context/Transform/PriceContext.php index 480d6cc98..075588f4b 100644 --- a/tests/Behat/Context/Transform/PriceContext.php +++ b/tests/Behat/Context/Transform/PriceContext.php @@ -9,12 +9,10 @@ final class PriceContext implements Context { /** - * @Transform /^"(\-)?((?:\d+\.)?\d+)"$/ + * @Transform /^"(\-)?(\d+(?:\.\d{1,2})?)"$/ */ public function getPriceFromString(string $sign, string $price): int { - $this->validatePriceString($price); - $price = (int) round((float) $price * 100, 2); if ('-' === $sign) { @@ -23,12 +21,4 @@ public function getPriceFromString(string $sign, string $price): int return $price; } - - /** @throws \InvalidArgumentException */ - private function validatePriceString(string $price): void - { - if (!(bool) preg_match('/^\d+(?:\.\d{1,2})?$/', $price)) { - throw new \InvalidArgumentException('Price string should not have more than 2 decimal digits.'); - } - } }