From 96a0ee7e5a50ab2096375fca9f2ae8e0430ca78c Mon Sep 17 00:00:00 2001 From: Adam Kasperczak Date: Wed, 11 Aug 2021 10:19:57 +0200 Subject: [PATCH] flag paid on invoice show and pdf --- UPGRADE.md | 63 ++++++++++++++++++- .../seeing_payment_state_on_invoice.feature | 32 ++++++++++ spec/Entity/InvoiceSpec.php | 1 + spec/Factory/InvoiceFactorySpec.php | 2 + spec/Generator/InvoiceGeneratorSpec.php | 3 + src/Entity/Invoice.php | 15 +++++ src/Entity/InvoiceInterface.php | 4 ++ src/Factory/InvoiceFactory.php | 2 + src/Factory/InvoiceFactoryInterface.php | 1 + src/Generator/InvoiceGenerator.php | 5 ++ src/Migrations/Version20210811092457.php | 35 +++++++++++ src/Resources/config/doctrine/Invoice.orm.xml | 1 + src/Resources/translations/messages.en.yml | 4 ++ .../views/Invoice/Download/pdf.html.twig | 11 ++++ src/Resources/views/Invoice/show.html.twig | 10 +++ .../Ui/Admin/ManagingInvoicesContext.php | 8 +++ tests/Behat/Page/Admin/Invoice/ShowPage.php | 6 ++ .../Page/Admin/Invoice/ShowPageInterface.php | 2 + 18 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 features/managing_invoices/seeing_payment_state_on_invoice.feature create mode 100644 src/Migrations/Version20210811092457.php diff --git a/UPGRADE.md b/UPGRADE.md index 8216bd5d..01e68db8 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,3 +1,64 @@ +### UPGRADE FROM 0.17.0 TO 0.18.0 + +Now on invoice admin and shop user can check if related order was paid before invoice generated. + +1. `src/Entity/Invoice.php` model has new field (`isPaid`), and updated constructor arguments: + + ```dif + public function __construct( + string $id, + string $number, + OrderInterface $order, + \DateTimeInterface $issuedAt, + BillingDataInterface $billingData, + string $currencyCode, + string $localeCode, + int $total, + Collection $lineItems, + Collection $taxItems, + ChannelInterface $channel, + + bool $isPaid, + InvoiceShopBillingDataInterface $shopBillingData + ) { + $this->id = $id; + $this->number = $number; + $this->order = $order; + $this->issuedAt = clone $issuedAt; + $this->billingData = $billingData; + $this->currencyCode = $currencyCode; + $this->localeCode = $localeCode; + $this->total = $total; + $this->lineItems = $lineItems; + $this->taxItems = $taxItems; + $this->channel = $channel; + + $this->isPaid = $isPaid; + $this->shopBillingData = $shopBillingData; + ``` + +1. New field on `src/Entity/Invoice.php` implies a database update + +1. Method `createForData` from `src/Factory/InvoiceFactory.php` service was updated: + + ```dif + public function createForData( + string $id, + string $number, + OrderInterface $order, + \DateTimeInterface $issuedAt, + BillingDataInterface $billingData, + string $currencyCode, + string $localeCode, + int $total, + Collection $lineItems, + Collection $taxItems, + ChannelInterface $channel, + + bool $isPaid, + InvoiceShopBillingDataInterface $shopBillingData = null + ): InvoiceInterface { + // ... + } + ``` + ### UPGRADE FROM 0.16.1 TO 0.17.0 Invoices are now saved on the server during their generation (by default, when the order is paid). @@ -8,7 +69,7 @@ Invoices are now saved on the server during their generation (by default, when t to `InvoiceFileProviderInterface $invoiceFileProvider` 1. `Sylius\InvoicingPlugin\Generator\InvoicePdfFileGenerator` class has additional `InvoiceFileNameGeneratorInterface $invoiceFileNameGenerator` dependency, placed on 4th place, before `string $template` -1. `Sylius\InvoicingPlugin\Ui\Action\DownloadInvoiceAction` class 4th dependency has been changed from `InvoicePdfFileGeneratorInterface $invoicePdfFileGenerator` +1. `Sylius\InvoicingPlugin\Ui\Action\DownloadInvoisrc/Resources/views/Invoice/show.html.twigceAction` class 4th dependency has been changed from `InvoicePdfFileGeneratorInterface $invoicePdfFileGenerator` to `InvoiceFileProviderInterface $invoiceFilePathProvider` 1. `Sylius\InvoicingPlugin\Converter\LineItemsConverter` class has additional `TaxRatePercentageProviderInterface $taxRatePercentageProvider` dependency diff --git a/features/managing_invoices/seeing_payment_state_on_invoice.feature b/features/managing_invoices/seeing_payment_state_on_invoice.feature new file mode 100644 index 00000000..4483e583 --- /dev/null +++ b/features/managing_invoices/seeing_payment_state_on_invoice.feature @@ -0,0 +1,32 @@ +@managing_invoices +Feature: Seeing payment state of an invoice + In order to see if customer paid order + As an Administrator + I want to be able to view payment state of invoice + + Background: + Given the store operates on a single channel in "United States" + And the store has "VAT" tax rate of 10% for "Clothes" within the "US" zone + And the store has a product "Angel T-Shirt" priced at "$60.00" + And it belongs to "Clothes" tax category + And the store has "UPS" shipping method with "$10.00" fee + And the store allows paying with "Cash on Delivery" + And the store allows paying with "Quick Payments" + And I am logged in as an administrator + And I set shop billing data for channel "United States" as "Ragnarok", "1100110011", "Pacific Coast Hwy", "90806" "Los Angeles", "United States" + And there is a customer "lucy@teamlucifer.com" that placed an order "#00000666" + And the customer bought 2 "Angel T-Shirt" products + And the customer "Lucifer Morningstar" addressed it to "Seaside Fwy", "90802" "Los Angeles" in the "United States" + And for the billing address of "Mazikeen Lilim" in the "Pacific Coast Hwy", "90806" "Los Angeles", "United States" + Given the customer chose "UPS" shipping method with "Cash on Delivery" payment + + @ui + Scenario: Seeing unpaid invoice details + When I view the summary of the invoice for order "#00000666" + And it should be unpaid + + @ui + Scenario: Seeing invoice details after payment made + Given this order is already paid + When I view the summary of the invoice for order "#00000666" + And it should be unpaid diff --git a/spec/Entity/InvoiceSpec.php b/spec/Entity/InvoiceSpec.php index ae0112b1..469c5127 100644 --- a/spec/Entity/InvoiceSpec.php +++ b/spec/Entity/InvoiceSpec.php @@ -48,6 +48,7 @@ function let( new ArrayCollection([$lineItem->getWrappedObject()]), new ArrayCollection([$taxItem->getWrappedObject()]), $channel, + true, $shopBillingData ); } diff --git a/spec/Factory/InvoiceFactorySpec.php b/spec/Factory/InvoiceFactorySpec.php index 30f99a92..d54fab7b 100644 --- a/spec/Factory/InvoiceFactorySpec.php +++ b/spec/Factory/InvoiceFactorySpec.php @@ -49,6 +49,7 @@ function it_creates_an_invoice_for_given_data( new ArrayCollection(), new ArrayCollection(), $channel, + true, $invoiceShopBillingData )->shouldReturnAnInstanceOf(InvoiceInterface::class); } @@ -72,6 +73,7 @@ function it_allows_for_nullable_shop_billing_data( new ArrayCollection(), new ArrayCollection(), $channel, + true, null )->shouldReturnAnInstanceOf(InvoiceInterface::class); } diff --git a/spec/Generator/InvoiceGeneratorSpec.php b/spec/Generator/InvoiceGeneratorSpec.php index 6ba9a430..0fe7f39e 100644 --- a/spec/Generator/InvoiceGeneratorSpec.php +++ b/spec/Generator/InvoiceGeneratorSpec.php @@ -18,6 +18,7 @@ use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\OrderInterface; +use Sylius\Component\Payment\Model\PaymentInterface; use Sylius\InvoicingPlugin\Converter\BillingDataConverterInterface; use Sylius\InvoicingPlugin\Converter\InvoiceShopBillingDataConverterInterface; use Sylius\InvoicingPlugin\Converter\LineItemsConverterInterface; @@ -89,6 +90,7 @@ function it_generates_an_invoice_for_a_given_order( $order->getLocaleCode()->willReturn('en_US'); $order->getTotal()->willReturn(10300); $order->getChannel()->willReturn($channel); + $order->getPaymentState()->willReturn(PaymentInterface::STATE_COMPLETED); $order->getBillingAddress()->willReturn($billingAddress); $billingDataConverter->convert($billingAddress)->willReturn($billingData); @@ -110,6 +112,7 @@ function it_generates_an_invoice_for_a_given_order( new ArrayCollection([$unitLineItem->getWrappedObject(), $shippingLineItem->getWrappedObject()]), new ArrayCollection([$taxItem->getWrappedObject()]), $channel, + true, $invoiceShopBillingData )->willReturn($invoice); diff --git a/src/Entity/Invoice.php b/src/Entity/Invoice.php index e6743d66..94446999 100644 --- a/src/Entity/Invoice.php +++ b/src/Entity/Invoice.php @@ -53,6 +53,9 @@ class Invoice implements InvoiceInterface /** @var ChannelInterface */ protected $channel; + /** @var bool */ + protected $isPaid; + /** @var InvoiceShopBillingDataInterface */ protected $shopBillingData; @@ -68,6 +71,7 @@ public function __construct( Collection $lineItems, Collection $taxItems, ChannelInterface $channel, + bool $isPaid, InvoiceShopBillingDataInterface $shopBillingData ) { $this->id = $id; @@ -81,6 +85,7 @@ public function __construct( $this->lineItems = $lineItems; $this->taxItems = $taxItems; $this->channel = $channel; + $this->isPaid = $isPaid; $this->shopBillingData = $shopBillingData; /** @var LineItemInterface $lineItem */ @@ -187,4 +192,14 @@ public function shopBillingData(): InvoiceShopBillingDataInterface { return $this->shopBillingData; } + + public function isPaid(): bool + { + return $this->isPaid; + } + + public function setIsPaid(bool $isPaid): void + { + $this->isPaid = $isPaid; + } } diff --git a/src/Entity/InvoiceInterface.php b/src/Entity/InvoiceInterface.php index 6b03b560..321413f5 100644 --- a/src/Entity/InvoiceInterface.php +++ b/src/Entity/InvoiceInterface.php @@ -50,4 +50,8 @@ public function taxesTotal(): int; public function channel(): ChannelInterface; public function shopBillingData(): InvoiceShopBillingDataInterface; + + public function isPaid(): bool; + + public function setIsPaid(bool $isPaid): void; } diff --git a/src/Factory/InvoiceFactory.php b/src/Factory/InvoiceFactory.php index f91d9d0f..4072c34e 100644 --- a/src/Factory/InvoiceFactory.php +++ b/src/Factory/InvoiceFactory.php @@ -36,6 +36,7 @@ public function createForData( Collection $lineItems, Collection $taxItems, ChannelInterface $channel, + bool $isPaid, InvoiceShopBillingDataInterface $shopBillingData = null ): InvoiceInterface { return new Invoice( @@ -50,6 +51,7 @@ public function createForData( $lineItems, $taxItems, $channel, + $isPaid, $shopBillingData ?? new InvoiceShopBillingData() ); } diff --git a/src/Factory/InvoiceFactoryInterface.php b/src/Factory/InvoiceFactoryInterface.php index 8a8e0fdc..668259ee 100644 --- a/src/Factory/InvoiceFactoryInterface.php +++ b/src/Factory/InvoiceFactoryInterface.php @@ -34,6 +34,7 @@ public function createForData( Collection $lineItems, Collection $taxItems, ChannelInterface $channel, + bool $isPaid, InvoiceShopBillingDataInterface $shopBillingData = null ): InvoiceInterface; } diff --git a/src/Generator/InvoiceGenerator.php b/src/Generator/InvoiceGenerator.php index aa814d85..e2a06c59 100644 --- a/src/Generator/InvoiceGenerator.php +++ b/src/Generator/InvoiceGenerator.php @@ -17,6 +17,7 @@ use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\OrderInterface; +use Sylius\Component\Payment\Model\PaymentInterface; use Sylius\InvoicingPlugin\Converter\BillingDataConverterInterface; use Sylius\InvoicingPlugin\Converter\InvoiceShopBillingDataConverterInterface; use Sylius\InvoicingPlugin\Converter\LineItemsConverterInterface; @@ -78,6 +79,9 @@ public function generateForOrder(OrderInterface $order, \DateTimeInterface $date /** @var ChannelInterface $channel */ $channel = $order->getChannel(); + /** @var bool $isPaid */ + $isPaid = $order->getPaymentState() === PaymentInterface::STATE_COMPLETED; + return $this->invoiceFactory->createForData( $this->uuidInvoiceIdentifierGenerator->generate(), $this->sequentialInvoiceNumberGenerator->generate(), @@ -93,6 +97,7 @@ public function generateForOrder(OrderInterface $order, \DateTimeInterface $date )), $this->taxItemsConverter->convert($order), $channel, + $isPaid, $this->invoiceShopBillingDataConverter->convert($channel) ); } diff --git a/src/Migrations/Version20210811092457.php b/src/Migrations/Version20210811092457.php new file mode 100644 index 00000000..544d2fd6 --- /dev/null +++ b/src/Migrations/Version20210811092457.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE sylius_invoicing_plugin_invoice ADD is_paid TINYINT(1) NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE sylius_invoicing_plugin_invoice DROP is_paid'); + } +} diff --git a/src/Resources/config/doctrine/Invoice.orm.xml b/src/Resources/config/doctrine/Invoice.orm.xml index 43a25936..afa772ff 100644 --- a/src/Resources/config/doctrine/Invoice.orm.xml +++ b/src/Resources/config/doctrine/Invoice.orm.xml @@ -9,6 +9,7 @@ + diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index 77aa6fcb..f557c9d4 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -16,6 +16,10 @@ sylius_invoicing_plugin: net_value: 'Net value' no: 'No.' order_number: 'Order number' + payment: + paid: 'Paid' + yes: 'Yes' + no: 'No' resend_invoice: 'Resend' seller: 'Seller' tax_amount: 'Tax amount' diff --git a/src/Resources/views/Invoice/Download/pdf.html.twig b/src/Resources/views/Invoice/Download/pdf.html.twig index 965fcc71..4279c012 100644 --- a/src/Resources/views/Invoice/Download/pdf.html.twig +++ b/src/Resources/views/Invoice/Download/pdf.html.twig @@ -125,6 +125,17 @@ {{ '%0.2f'|format(invoice.total/100) }} {{ invoice.currencyCode }} + + + {{ 'sylius_invoicing_plugin.ui.payment.paid'|trans }}: + + {% if invoice.isPaid() %} + {{ 'sylius_invoicing_plugin.ui.payment.yes'|trans }} + {% else %} + {{ 'sylius_invoicing_plugin.ui.payment.no'|trans }} + {% endif %} + + {% if invoice.taxItems.count() > 0 %} diff --git a/src/Resources/views/Invoice/show.html.twig b/src/Resources/views/Invoice/show.html.twig index 9be6a08f..ff6056fb 100644 --- a/src/Resources/views/Invoice/show.html.twig +++ b/src/Resources/views/Invoice/show.html.twig @@ -146,5 +146,15 @@ +
+
+ {{ 'sylius_invoicing_plugin.ui.payment.paid'|trans }}: + {% if invoice.isPaid() %} + {{ 'sylius_invoicing_plugin.ui.payment.yes'|trans }} + {% else %} + {{ 'sylius_invoicing_plugin.ui.payment.no'|trans }} + {% endif %} +
+
{% endblock %} diff --git a/tests/Behat/Context/Ui/Admin/ManagingInvoicesContext.php b/tests/Behat/Context/Ui/Admin/ManagingInvoicesContext.php index 01eb26c6..8b235740 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingInvoicesContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingInvoicesContext.php @@ -332,4 +332,12 @@ public function itShouldHaveItemsWithUnitPriceNetValueTaxTotalAndTotalInCurrency ): void { Assert::true($this->showPage->hasItemWithData($name, $unitPrice, $quantity, $taxTotal, $total, $currencyCode, $netValue)); } + + /** + * @Then it should be unpaid + */ + public function itShouldBeUnpaid(): void + { + Assert::false($this->showPage->isPaid()); + } } diff --git a/tests/Behat/Page/Admin/Invoice/ShowPage.php b/tests/Behat/Page/Admin/Invoice/ShowPage.php index 1a1aaee6..8312e21a 100644 --- a/tests/Behat/Page/Admin/Invoice/ShowPage.php +++ b/tests/Behat/Page/Admin/Invoice/ShowPage.php @@ -157,6 +157,11 @@ public function goBack(): void $this->getElement('back')->click(); } + public function isPaid(): bool + { + return str_contains($this->getElement('paid')->getHtml(), 'Yes'); + } + protected function getDefinedElements(): array { return array_merge(parent::getDefinedElements(), [ @@ -170,6 +175,7 @@ protected function getDefinedElements(): array 'invoice_total' => '[data-test-invoice-total]', 'invoice_total_currency_code' => '[data-test-invoice-total-currency-code]', 'issued_at' => '#invoice-issued-at', + 'paid' => '[data-test-invoice-is-paid]', 'shop_billing_data' => '#shop-billing-data', 'table' => '.table', ]); diff --git a/tests/Behat/Page/Admin/Invoice/ShowPageInterface.php b/tests/Behat/Page/Admin/Invoice/ShowPageInterface.php index 4accd4a2..6fba6dce 100644 --- a/tests/Behat/Page/Admin/Invoice/ShowPageInterface.php +++ b/tests/Behat/Page/Admin/Invoice/ShowPageInterface.php @@ -54,4 +54,6 @@ public function download(): void; public function resend(): void; public function goBack(): void; + + public function isPaid(): bool; }