From 069ead1e768862adec4cdbbb514821dcf53128d9 Mon Sep 17 00:00:00 2001 From: Fernando Caraballo Ortiz Date: Wed, 20 Jan 2016 12:10:11 +0000 Subject: [PATCH] reports correct sum with currencies and label --- UPGRADE.md | 1 + app/migrations/Version20160118140609.php | 34 +++++++++++++ behat.yml.dist | 1 + features/reports/reports.feature | 50 +++++++++++++++++-- .../Bundle/CoreBundle/Behat/CoreContext.php | 11 +++- .../CoreBundle/Checkout/Step/FinalizeStep.php | 11 +++- .../Doctrine/ORM/OrderRepository.php | 3 +- .../EventListener/OrderCurrencyListener.php | 6 +++ .../config/doctrine/model/Order.orm.xml | 1 + .../CoreBundle/Resources/config/services.xml | 1 + .../DataFetcher/SalesTotalDataFetcherSpec.php | 2 +- .../DataFixtures/ORM/LoadOrdersData.php | 9 +++- .../ReportBundle/Behat/ReportContext.php | 30 +++++++++++ .../Controller/ReportController.php | 5 ++ .../Currency/ChannelAwareCurrencyProvider.php | 13 +++-- .../Currency/Provider/CurrencyProvider.php | 9 ++++ .../Provider/CurrencyProviderInterface.php | 6 +++ 17 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 app/migrations/Version20160118140609.php diff --git a/UPGRADE.md b/UPGRADE.md index 262585e0e4..370968041f 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -226,6 +226,7 @@ class BookController extends ResourceController * Removed ``Address`` relations to ``Country`` and ``Province`` objects, their unique ``code`` is used instead; * Removed specific ``ZoneMembers`` i.e. ``ProvinceZoneMember`` in favor of a dynamic ``ZoneMember``; * https://github.com/Sylius/Sylius/pull/3696 +* ``exchangeRate`` is now recorded for ``Order`` at time of purchase for accurate cross-currency reporting. ### Order and SyliusOrderBundle diff --git a/app/migrations/Version20160118140609.php b/app/migrations/Version20160118140609.php new file mode 100644 index 0000000000..4f979a9f90 --- /dev/null +++ b/app/migrations/Version20160118140609.php @@ -0,0 +1,34 @@ +abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE sylius_order ADD exchange_rate NUMERIC(10, 5) NOT NULL'); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE sylius_order DROP exchange_rate'); + } +} diff --git a/behat.yml.dist b/behat.yml.dist index 1269019653..bfaa387204 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -250,6 +250,7 @@ default: - Behat\MinkExtension\Context\MinkContext - Sylius\Bundle\CoreBundle\Behat\HookContext - Sylius\Bundle\AddressingBundle\Behat\AddressingContext + - Sylius\Bundle\ShippingBundle\Behat\ShippingContext - Sylius\Bundle\CoreBundle\Behat\CoreContext - Sylius\Bundle\PaymentBundle\Behat\PaymentContext - Sylius\Bundle\ProductBundle\Behat\ProductContext diff --git a/features/reports/reports.feature b/features/reports/reports.feature index b9f97e1efd..4cec0d0ec2 100644 --- a/features/reports/reports.feature +++ b/features/reports/reports.feature @@ -9,6 +9,7 @@ Feature: Reports And there are following reports configured: | code | name | description | renderer | renderer_configuration | data_fetcher | data_fetcher_configuration | | table_report | TableReport | Lorem ipsum | table | Template:SyliusReportBundle:Table:default.html.twig | user_registration | Period:day,Start:2010-01-01,End:2010-04-01 | + | sales_report | SalesReport | Lorem ipsum | table | Template:SyliusReportBundle:Table:default.html.twig | sales_total | Period:day,Start:2010-01-01,End:2010-01-05 | | bar_chart_report | BarChartReport | Lorem ipsum | chart | Type:bar,Template:SyliusReportBundle:Chart:default.html.twig | user_registration | Period:month,Start:2010-01-01,End:2010-04-01 | And there are following users: | email | enabled | created at | @@ -20,13 +21,13 @@ Feature: Reports Scenario: Seeing created reports it the list Given I am on the dashboard page When I follow "Reports" - Then I should see 2 reports in the list + Then I should see 3 reports in the list Scenario: Adding new report with default options Given I am on the report creation page When I fill in the following: | Code | report1 | - | Name | Report1 | + | Name | Report1 | | Description | Lorem ipsum dolor | And I press "Create" Then I should be on the page of report "Report1" @@ -59,7 +60,7 @@ Feature: Reports And I select "1" from "sylius_report_dataFetcherConfiguration_start_day" And I select "3" from "sylius_report_dataFetcherConfiguration_end_day" And I select "month" from "Time period" - And I press "Create" + And I press "Create" Then I should be on the page of report "Report3" And I should see "Report has been successfully created" And I should see 1 results in the list @@ -88,7 +89,7 @@ Feature: Reports Given I am on the report index page When I press "Details" near "TableReport" Then I should be on the page of report "TableReport" - + Scenario: Accessing report edit form from list Given I am on the report index page When I press "Edit" near "TableReport" @@ -140,3 +141,44 @@ Feature: Reports And I press "Create" Then I should still be on the report creation page And I should see "Report code cannot be blank" + + Scenario: Getting the correct sum with exchange rate + Given the following zones are defined: + | name | type | members | + | Scandinavia | country | Norway, Sweden, Finland | + | France | country | France | + And there are following shipping categories: + | code | name | + | SC1 | Regular | + | SC2 | Heavy | + And the following shipping methods exist: + | code | category | zone | name | + | SM1 | Regular | Scandinavia | DHL | + | SM2 | Heavy | France | UPS | + And the following products exist: + | name | price | sku | + | Mug | 10 | 456 | + | Book | 22 | 948 | + And the following orders exist: + | customer | shipment | address | currency | exchange_rate | + | sylius@example.com | UPS, shipped, DTBHH380HG | Théophile Morel, 17 avenue Jean Portalis, 33000, Bordeaux, France | EUR | 1.00000 | + | linustorvalds@linux.com | DHL, shipped, DTBHH380HH | Linus Torvalds, Väätäjänniementie 59, 00440, Helsinki, Finland | USD | 0.5 | + | sylius@example.com | UPS, shipped, DTBHH380HI | Théophile Morel, 17 avenue Jean Portalis, 33000, Bordeaux, France | GBP | 1.25 | + And order #000000001 has following items: + | product | quantity | + | Mug | 2 | + | Book | 1 | + And order #000000002 has following items: + | product | quantity | + | Mug | 2 | + | Book | 1 | + And order #000000003 has following items: + | product | quantity | + | Mug | 2 | + | Book | 1 | + And order #000000001 will be completed on "2010-01-01" + And order #000000002 will be completed on "2010-01-02" + And order #000000003 will be completed on "2010-01-02" + When I am on the page of report "SalesReport" + Then the report row for date "2010-01-01" will have a total amount of "42" + Then the report row for date "2010-01-02" will have a total amount of "73.50" \ No newline at end of file diff --git a/src/Sylius/Bundle/CoreBundle/Behat/CoreContext.php b/src/Sylius/Bundle/CoreBundle/Behat/CoreContext.php index f662329771..63afd9aa31 100644 --- a/src/Sylius/Bundle/CoreBundle/Behat/CoreContext.php +++ b/src/Sylius/Bundle/CoreBundle/Behat/CoreContext.php @@ -149,7 +149,16 @@ class CoreContext extends DefaultContext $this->createPayment($order, $paymentMethod); - $order->setCurrency('EUR'); + if (isset($data['currency']) && '' !== trim($data['currency'])) { + $order->setCurrency($data['currency']); + } else { + $order->setCurrency('EUR'); + } + + if (isset($data['exchange_rate']) && '' !== trim($data['exchange_rate'])) { + $order->setExchangeRate($data['exchange_rate']); + } + $order->setPaymentState(PaymentInterface::STATE_COMPLETED); $order->complete(); diff --git a/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php b/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php index 54921dab6a..342dc1d24a 100644 --- a/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php +++ b/src/Sylius/Bundle/CoreBundle/Checkout/Step/FinalizeStep.php @@ -21,6 +21,7 @@ use Sylius\Component\Order\OrderTransitions; * Final checkout step. * * @author Paweł Jędrzejewski + * @author Fernando Caraballo Ortiz */ class FinalizeStep extends CheckoutStep { @@ -64,11 +65,19 @@ class FinalizeStep extends CheckoutStep protected function completeOrder(OrderInterface $order) { $this->get('session')->set('sylius_order_id', $order->getId()); + + $currencyProvider = $this->get('sylius.currency_provider'); + $this->dispatchCheckoutEvent(SyliusOrderEvents::PRE_CREATE, $order); $this->dispatchCheckoutEvent(SyliusCheckoutEvents::FINALIZE_PRE_COMPLETE, $order); $this->get('sm.factory')->get($order, OrderTransitions::GRAPH)->apply(OrderTransitions::SYLIUS_CREATE, true); - + if ($order->getCurrency() !== $currencyProvider->getBaseCurrency()) { + $currencyRepository = $this->get('sylius.repository.currency'); + $currency = $currencyRepository->findOneBy(['code' => $order->getCurrency()]); + $order->setExchangeRate($currency->getExchangeRate()); + } + $manager = $this->get('sylius.manager.order'); $manager->persist($order); $manager->flush(); diff --git a/src/Sylius/Bundle/CoreBundle/Doctrine/ORM/OrderRepository.php b/src/Sylius/Bundle/CoreBundle/Doctrine/ORM/OrderRepository.php index 1ffcd869d7..43d61f7d91 100644 --- a/src/Sylius/Bundle/CoreBundle/Doctrine/ORM/OrderRepository.php +++ b/src/Sylius/Bundle/CoreBundle/Doctrine/ORM/OrderRepository.php @@ -330,8 +330,9 @@ class OrderRepository extends CartRepository implements OrderRepositoryInterface $configuration['end'], $groupBy); + $baseCurrencyCode = $configuration['baseCurrency'] ? 'in ' . $configuration['baseCurrency']->getCode() : ''; $queryBuilder - ->select('DATE(o.completed_at) as date', 'TRUNCATE(SUM(o.total)/ 100,2) as "total sum"') + ->select('DATE(o.completed_at) as date', 'TRUNCATE(SUM(o.total * o.exchange_rate)/ 100,2) as "total sum ' . $baseCurrencyCode . '"') ; return $queryBuilder diff --git a/src/Sylius/Bundle/CoreBundle/EventListener/OrderCurrencyListener.php b/src/Sylius/Bundle/CoreBundle/EventListener/OrderCurrencyListener.php index 8b4da5a0d7..c7d0b02c16 100644 --- a/src/Sylius/Bundle/CoreBundle/EventListener/OrderCurrencyListener.php +++ b/src/Sylius/Bundle/CoreBundle/EventListener/OrderCurrencyListener.php @@ -23,8 +23,14 @@ use Symfony\Component\EventDispatcher\GenericEvent; */ class OrderCurrencyListener { + /** + * @var CurrencyContextInterface + */ protected $currencyContext; + /** + * @param CurrencyContextInterface $currencyContext + */ public function __construct(CurrencyContextInterface $currencyContext) { $this->currencyContext = $currencyContext; diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/Order.orm.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/Order.orm.xml index e341425141..75db357a82 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/Order.orm.xml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/Order.orm.xml @@ -11,6 +11,7 @@ + diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml index af82f73e14..a7a38fcdf9 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml @@ -468,6 +468,7 @@ + diff --git a/src/Sylius/Bundle/CoreBundle/spec/DataFetcher/SalesTotalDataFetcherSpec.php b/src/Sylius/Bundle/CoreBundle/spec/DataFetcher/SalesTotalDataFetcherSpec.php index 9cfc08500c..704da83782 100644 --- a/src/Sylius/Bundle/CoreBundle/spec/DataFetcher/SalesTotalDataFetcherSpec.php +++ b/src/Sylius/Bundle/CoreBundle/spec/DataFetcher/SalesTotalDataFetcherSpec.php @@ -125,7 +125,7 @@ class SalesTotalDataFetcherSpec extends ObjectBehavior $this->fetch($configuration)->shouldBeLike($data); } - public function it_does_not_allowed_wrond_data_period($orderRepository, Data $data) + public function it_does_not_allowed_wrond_data_period() { $configuration = array( 'start' => new \DateTime('2010-01-01 00:00:00.000000'), diff --git a/src/Sylius/Bundle/FixturesBundle/DataFixtures/ORM/LoadOrdersData.php b/src/Sylius/Bundle/FixturesBundle/DataFixtures/ORM/LoadOrdersData.php index 22b6a04417..c66d8b3ba9 100644 --- a/src/Sylius/Bundle/FixturesBundle/DataFixtures/ORM/LoadOrdersData.php +++ b/src/Sylius/Bundle/FixturesBundle/DataFixtures/ORM/LoadOrdersData.php @@ -40,6 +40,12 @@ class LoadOrdersData extends DataFixture 'MOBILE', ); + $currencyExchangeRates = array( + 'GBP' => 0.8, + 'USD' => 1.2, + 'EUR' => 1.0 + ); + for ($i = 1; $i <= 50; $i++) { /* @var $order OrderInterface */ $order = $orderFactory->createNew(); @@ -62,7 +68,8 @@ class LoadOrdersData extends DataFixture $this->createShipment($order); - $order->setCurrency($this->faker->randomElement(array('EUR', 'USD', 'GBP'))); + $order->setCurrency($this->faker->randomElement(array_keys($currencyExchangeRates))); + $order->setExchangeRate($currencyExchangeRates[$order->getCurrency()]); $order->setShippingAddress($this->createAddress()); $order->setBillingAddress($this->createAddress()); $order->setCreatedAt($this->faker->dateTimeBetween('1 year ago', 'now')); diff --git a/src/Sylius/Bundle/ReportBundle/Behat/ReportContext.php b/src/Sylius/Bundle/ReportBundle/Behat/ReportContext.php index febbbc1340..c67262c0c9 100644 --- a/src/Sylius/Bundle/ReportBundle/Behat/ReportContext.php +++ b/src/Sylius/Bundle/ReportBundle/Behat/ReportContext.php @@ -18,6 +18,7 @@ use Sylius\Bundle\ResourceBundle\Behat\DefaultContext; * ReportContext for ReportBundle scenarios. * * @author Mateusz Zalewski + * @author Fernando Caraballo Ortiz */ class ReportContext extends DefaultContext { @@ -96,4 +97,33 @@ class ReportContext extends DefaultContext return $report; } + + /** + * @Then the report row for date :date will have a total amount of :total + */ + public function theReportRowForDateWillHaveATotalAmountOf($date, $total) + { + $page = $this->getSession()->getPage(); + $rows = $page->findAll('css', 'table tbody tr'); + foreach ($rows as $row) { + $columns = $row->findAll('css', 'td'); + if ($columns[1]->getText() === $date) { + \PHPUnit_Framework_Assert::assertEquals($columns[2]->getText(), $total); + } + } + } + + /** + * @Given order #:orderNumber will be completed on :date + */ + public function orderWillBeCompletedOn($orderNumber, $date) + { + $orderRepository = $this->getContainer()->get('sylius.repository.cart'); + $orderManager = $this->getContainer()->get('sylius.manager.cart'); + + $order = $orderRepository->findOneBy(['number' => $orderNumber]); + $order->setCompletedAt(new \DateTime($date)); + + $orderManager->flush(); + } } diff --git a/src/Sylius/Bundle/ReportBundle/Controller/ReportController.php b/src/Sylius/Bundle/ReportBundle/Controller/ReportController.php index 1e6689dfda..8b4841a11d 100644 --- a/src/Sylius/Bundle/ReportBundle/Controller/ReportController.php +++ b/src/Sylius/Bundle/ReportBundle/Controller/ReportController.php @@ -22,6 +22,7 @@ use Symfony\Component\HttpFoundation\Response; /** * @author Mateusz Zalewski * @author Łukasz Chruściel + * @author Fernando Caraballo Ortiz */ class ReportController extends ResourceController { @@ -63,6 +64,8 @@ class ReportController extends ResourceController */ public function embedAction(Request $request, $report, array $configuration = array()) { + $currencyProvider = $this->get('sylius.currency_provider'); + if (!$report instanceof ReportInterface) { $report = $this->getReportRepository()->findOneBy(array('code' => $report)); } @@ -72,6 +75,8 @@ class ReportController extends ResourceController } $configuration = $request->query->get('configuration', $configuration); + $configuration['baseCurrency'] = $currencyProvider->getBaseCurrency(); + $data = $this->getReportDataFetcher()->fetch($report, $configuration); return new Response($this->getReportRenderer()->render($report, $data)); diff --git a/src/Sylius/Component/Core/Currency/ChannelAwareCurrencyProvider.php b/src/Sylius/Component/Core/Currency/ChannelAwareCurrencyProvider.php index 27bee0c91d..0ada2e7323 100644 --- a/src/Sylius/Component/Core/Currency/ChannelAwareCurrencyProvider.php +++ b/src/Sylius/Component/Core/Currency/ChannelAwareCurrencyProvider.php @@ -13,27 +13,30 @@ namespace Sylius\Component\Core\Currency; use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Currency\Model\CurrencyInterface; -use Sylius\Component\Currency\Provider\CurrencyProviderInterface; +use Sylius\Component\Currency\Provider\CurrencyProvider; +use Sylius\Component\Resource\Repository\RepositoryInterface; /** * Currency provider, which returns currencies enabled for this channel. * * @author Paweł Jędrzejewski + * @author Fernando Caraballo Ortiz */ -class ChannelAwareCurrencyProvider implements CurrencyProviderInterface +class ChannelAwareCurrencyProvider extends CurrencyProvider { /** - * Channel context. - * * @var ChannelContextInterface */ protected $channelContext; /** * @param ChannelContextInterface $channelContext + * @param RepositoryInterface $currencyRepository */ - public function __construct(ChannelContextInterface $channelContext) + public function __construct(ChannelContextInterface $channelContext, RepositoryInterface $currencyRepository) { + parent::__construct($currencyRepository); + $this->channelContext = $channelContext; } diff --git a/src/Sylius/Component/Currency/Provider/CurrencyProvider.php b/src/Sylius/Component/Currency/Provider/CurrencyProvider.php index 90eb0bc7de..e904828405 100644 --- a/src/Sylius/Component/Currency/Provider/CurrencyProvider.php +++ b/src/Sylius/Component/Currency/Provider/CurrencyProvider.php @@ -15,6 +15,7 @@ use Sylius\Component\Resource\Repository\RepositoryInterface; /** * @author Paweł Jędrzejewski + * @author Fernando Caraballo Ortiz */ class CurrencyProvider implements CurrencyProviderInterface { @@ -38,4 +39,12 @@ class CurrencyProvider implements CurrencyProviderInterface { return $this->currencyRepository->findBy(array('enabled' => true)); } + + /** + * {@inheritdoc} + */ + public function getBaseCurrency() + { + return $this->currencyRepository->findOneBy(['base' => true]); + } } diff --git a/src/Sylius/Component/Currency/Provider/CurrencyProviderInterface.php b/src/Sylius/Component/Currency/Provider/CurrencyProviderInterface.php index c8e1eacb19..fa2550a70f 100644 --- a/src/Sylius/Component/Currency/Provider/CurrencyProviderInterface.php +++ b/src/Sylius/Component/Currency/Provider/CurrencyProviderInterface.php @@ -15,6 +15,7 @@ use Sylius\Component\Currency\Model\CurrencyInterface; /** * @author Paweł Jędrzejewski + * @author Fernando Caraballo Ortiz */ interface CurrencyProviderInterface { @@ -22,4 +23,9 @@ interface CurrencyProviderInterface * @return CurrencyInterface[] */ public function getAvailableCurrencies(); + + /** + * @return CurrencyInterface + */ + public function getBaseCurrency(); }