From 9586a6de30de76d1dacf6808b7c6e547aabb705a Mon Sep 17 00:00:00 2001 From: Hotlander Date: Fri, 20 Mar 2020 09:18:20 +0100 Subject: [PATCH] Squashed 'package/marello/' changes from 9051b1fde..1999cc10e 1999cc10e - removed internal version in Marello EE - updated not merged translations - updated version number in EE Readme - updated dev dependencies in EE / CE applications - updated author name in packages's composer.json 7e9afaad3 Merge branch 'develop' into test 0019e8516 Merge branch '3.0' into test 053da4351 Merge commit '4531bc1f6d96c73cf6bd961956a99e01a0f6ce64' into test 20b013feb Merge commit '2819c3c3081048e75dd1f381ed7719f52f1a4d62' into test e6c6ba176 Merge branch '3.0' into develop 2a921f8bc - updated environments with :delegated parameter on docker env - added 'final' steps on workflows - removed deletion of old customer data in order to preserve possible extended fields and keep issues with old foreign keys at bay - updated UPGRADE file in order to specify additional step for Marello upgrade to 3.0 for smooth transition - updated README of EE in order to update the current requirements for Marello 3.0 applications 91aed6a26 - Added migration to include organization id on the purchase order item 4e7ad22a5 - changed path of Invoice template to MarelloInvoiceBundle:Pdf:invoice.html.twig instead of MarelloPdfBundle:Download:invoice.html.twig 7493d2d51 - removed .class parameters from service container and replaced it with the FQCNs 7382a658f - removed marello_product.entity.class parameter from service container and replaced it with the FQCN d679bd298 - removed marello_catalog.category.entity.class parameter from service container and replaced it with the FQCN d2e87f5a0 - Merged improvements for PDF capabilities - Updated entities with SalesChannelAwareInterface who were already had a 'getSalesChannel()' method available - Renamed the ChannelAwareInterface to SalesChannelAwareInterface for clarity and consistency - Renamed the old SalesChannelAwareInterface to SalesChannelsAwareInterface to indicate the differences in interfaces, this one will have functions to add, remove and get multiple SalesChannels instead of a single SalesChannel bbf61e119 - PSR2 CS fixes in PDF/InvoiceBundle c72351a60 merged branch 3.0 into feature/MAR10001-849 ac4723293 MAR10001-849: PDF Bundle improvements 84ddeebb2 - updated text-extended composer script to run a predefined phpcs.xml - updated packingslip datagrid to fix the issue with the warehouse filter and sorter - updated config yml files in order to fix the issue with API test failing a4b158042 - Updated ProductAttributeControllerTest to test the index but not the actual creation of the attributes, during issues with creating multiple fields - Enabled index test of the ShippingMethodConfigRule of the ShippingMethodConfigsRuleControllerTest as we forgot to enable it after debugging issue with the failed test, also removed debugging logs 3b4f87c44 - Fixed issue with entity classes defined in the service container instead of using the FQCN 27539a2ed - updated unique keys for certain entities in order to have both the schema's and annotations in line with each other - updated delete operation tests to use the FQCN instead of a service container parameter as entity class 1d50767a4 - Added missing translation labels 704e2f7bc - updated check if template is not null to be more strict in the EmailTemplateManager - updated SendProcessorTest with setting the correct exception when trying to find a localized template and there is no result 4d821e901 - Updated unit tests to use and test the new underlying structure of resizing images f21174495 - Fixed AvailableInventoryValidatorTest by using the correct constructor arguments during the removal of the setDispatcher method 976f6bd94 - fixed OrderExtensionTest since the OrderItemStatuses have become enum's which are hard to mock during the nature of enums being extended entities, overcoming the issue by creating a stub class which will allow us to mock the class 03c2e3057 - updated unit test with the removal of the warehouse constructor argument for the InventoryItem as this has been removed as it was deprecated 259787bfc - Added phpcs.xml file for removing multiple arguments from commandline when running phpcs - updated dev.json to use new arguments when testing psr - fixed last CS of PSR2 since ORM data migrations are now being included in the config b4b1e20ff - 100% fixed PSR2 issues on the Marello CE edition, excluding the Tests/Migrations directories 65daa404e - 65% done on PSR2 PHPCS fixes - updated command that will execute the PSR check to exclude migrations and test directories - [WIP]: add phpcs.xml to root directory for a more clean configuration instead of stuffing the command line with .... 72e67d496 Merge branch 'feature/MAR10001-847' into task/MAR10001-0-merge-fixing-price-columns c2d088017 - merged updated inventory batch layout and moved some widgets around - updated data migrations of shipping/payment integrations because of the removal of the entity parameter classes of Oro 403f7d82f Merge branch 'feature/MAR10001-837' into 3.0 139c38dfc MAR10001-847: Fix prices in product datagrid 6d8be00d0 - updated to platform 4.1.0 - fixed reference to non-existent parameter 'oro_note.entity.class' 185d34b24 Merge branch 'feature/MAR10001-845' into 3.0 bcd1cac31 MAR10001-837: Inventory batch layout 2c9235aed - Added schema installer for ServicePointBundle - Updated installer of invoice bundle during installation errors on Postgres - Reverted changes made on Product datagrid during not showing products on the product grid c834a8ab7 MAR10001-845: Update WHG and SCG datagrids with the joins in a datagrid listener b6cd772dd - updated navigation items by swapping facilities and servicepoints in the menu - removed the height of business hours override td table in favor of restoring the height in the workflow display in page titles where applicable - added quick product navigation to purchase order item grid 2595ee11e Merge branch '3.0' of https://github.com/marellocommerce/development-mono-repository into 3.0 7ae8042af - removed empty line from services.yml ce975e1e4 Merge branch 'feature/MAR10001-842' into 3.0 cb30cc441 - removed deprecations from different classes, some inf favor of constructor dependency injection - fixed default user&password for initializing db's in the docker container 97faf3a6a Merge branch 'feature/MAR10001-776' into 3.0 026b5ed30 Merge branch 'feature/MAR10001-776' into 3.0 c21d40af7 MAR10001-776: Inventory Batches - fixed bug in replenishment 4531bc1f6 - added composer.lock 4487e16f3 - preparations for release 9c7ebb089 - preparations for release 1f7345af5 - preparations for release 2819c3c30 Merge branch 'feature/MAR10001-776' into 3.0 9c4bb6a27 Merge branch 'feature/MAR10001-776' into 3.0 49cb95939 Merge branch 'feature/MAR10001-776' into 3.0 928594e30 - fixed issue with results of the query being used when filtering products through the grid and trying to assign them to saleschannels - added ability to split the message for bigger chunks of data instead of having a single message with a lot of product ids - restructured the code in order to use the entity repository of the products instead of relying on the cached query from the querybuilder - Fixed the main issue of the bug where we couldn't mass-assign products to SalesChannels because the query for the products grid evolved in such a query, which made hard to reset parts of the query for counting and not having to deal with the query cache once the results where fetched 37cd32a84 - fixed issue with results of the query being used when filtering products through the grid and trying to assign them to saleschannels - added ability to split the message for bigger chunks of data instead of having a single message with a lot of product ids - restructured the code in order to use the entity repository of the products instead of relying on the cached query from the querybuilder - Fixed the main issue of the bug where we couldn't mass-assign products to SalesChannels because the query for the products grid evolved in such a query, which made hard to reset parts of the query for counting and not having to deal with the query cache once the results where fetched e191aafb4 MAR10001-842: Issue when creating Order when Google Address Geocoding is enabled 4db583582 - fixed issue with assigning saleschannel mass action where it wouldn't display the grid to select saleschannels to assign products to 93bc2e239 - fixed issue with assigning saleschannel mass action where it wouldn't display the grid to select saleschannels to assign products to 1fee5e261 - fixed issue with foreign key update on inventory_item_id in the inventory_level_log table - added migration to update warehouseName on existing inventorylevelLogs 7956cf253 - fixed issue with foreign key update on inventory_item_id in the inventory_level_log table - added migration to update warehouseName on existing inventorylevelLogs ca6f502a5 MAR10001-776: Inventory Batches - InventoryBatches combined in replenishment orders 13612475f MAR10001-776: Inventory Batches - InventoryBatches combined in replenishment orders 8a50d6ae6 MAR10001-776: Inventory Batches - InventoryBatches combined in replenishment orders 9967a85a7 [WIP] - working on the migration from existing inventory level logs to the new data structure for it - fixing issue where the inventory level log is being removed as soon as an inventory level is deleted - added additional columns to the inventorylevellog in order to be able to have a history of movements for the whole inventory item - updated data grid in order to display the log based on different columns and identifiers - updated enterprise grid listener for displaying different column as warehouse label - added warehouseName as human readable reference of which inventory level it was a movement of - removed FK on inventory level in order to keep the inventorylevel log df1cb7464 [WIP] - working on the migration from existing inventory level logs to the new data structure for it - fixing issue where the inventory level log is being removed as soon as an inventory level is deleted - added additional columns to the inventorylevellog in order to be able to have a history of movements for the whole inventory item - updated data grid in order to display the log based on different columns and identifiers - updated enterprise grid listener for displaying different column as warehouse label - added warehouseName as human readable reference of which inventory level it was a movement of - removed FK on inventory level in order to keep the inventorylevel log 4ea003c01 [WIP] - working on the migration from existing inventory level logs to the new data structure for it - fixing issue where the inventory level log is being removed as soon as an inventory level is deleted - added additional columns to the inventorylevellog in order to be able to have a history of movements for the whole inventory item - updated data grid in order to display the log based on different columns and identifiers - updated enterprise grid listener for displaying different column as warehouse label - added warehouseName as human readable reference of which inventory level it was a movement of - removed FK on inventory level in order to keep the inventorylevel log 5be051dd3 - fixed AddressListener event which had typo's in the service definition which caused the address not being rendered in the Order datagrid 0bb927d39 - fixed AddressListener event which had typo's in the service definition which caused the address not being rendered in the Order datagrid 9d20701b2 - updated EmailTemplateManager with correct method call on the EmailTemplateRepository which was lost during merge ce37a6112 - updated EmailTemplateManager with correct method call on the EmailTemplateRepository which was lost during merge 94e6f5587 - updated InventoryBatchCollection options to `allow_add` and ensure users are able to manually add batches - Added an add InventoryBatch button to the bottom of the InventoryBatchCollection 944a0499c - updated InventoryBatchCollection options to `allow_add` and ensure users are able to manually add batches - Added an add InventoryBatch button to the bottom of the InventoryBatchCollection 12b60eba1 - fixed issue with InventoryManager and InventoryLevelFixture where they used to initialize an InventoryItem with a warehouse, which is not necessary anymore - Added action button for enabling Inventory Batches via a button instead of having a checkbox in the form 666ab51f6 - fixed issue with InventoryManager and InventoryLevelFixture where they used to initialize an InventoryItem with a warehouse, which is not necessary anymore - Added action button for enabling Inventory Batches via a button instead of having a checkbox in the form 8ccaba9f6 - Merge branch 'feature/MAR10001-776' into feature/update-to-rc3 - Removed deprecated property 'warehouse' from InventoryItem, constructor of InventoryItem and getters and setters for the property 5d6d3844a - Merge branch 'feature/MAR10001-776' into feature/update-to-rc3 - Removed deprecated property 'warehouse' from InventoryItem, constructor of InventoryItem and getters and setters for the property 5b31fe9a8 - Merge branch 'feature/MAR10001-776' into feature/update-to-rc3 - Removed deprecated property 'warehouse' from InventoryItem, constructor of InventoryItem and getters and setters for the property 7f77e9197 MAR10001-776: Inventory Batches - created Inventory Balance trigger in order to update balanced inventory - added manual creation of InventoryBatch on the inventory batch update page d52df0655 MAR10001-776: Inventory Batches - created Inventory Balance trigger in order to update balanced inventory - added manual creation of InventoryBatch on the inventory batch update page cf018838d - updated applications (most used application variations) with new OroRequirements which require at least PHP 7.1.13 - updated dev.lock files with OroPlatform 4.1.0-rc4 and related packages (calendar, platform-enterprise etc.) - updated docker-compose files to include php 7.3.13 as version to build - updated .gitignore files to include the package.json - add package-lock.json files as this will be the locked npm package versions file - updated activity_list.provider tag to include FQCN for the activity c42a3a418 - updated applications (most used application variations) with new OroRequirements which require at least PHP 7.1.13 - updated dev.lock files with OroPlatform 4.1.0-rc4 and related packages (calendar, platform-enterprise etc.) - updated docker-compose files to include php 7.3.13 as version to build - updated .gitignore files to include the package.json - add package-lock.json files as this will be the locked npm package versions file - updated activity_list.provider tag to include FQCN for the activity cc3cbaebc - updated applications (most used application variations) with new OroRequirements which require at least PHP 7.1.13 - updated dev.lock files with OroPlatform 4.1.0-rc4 and related packages (calendar, platform-enterprise etc.) - updated docker-compose files to include php 7.3.13 as version to build - updated .gitignore files to include the package.json - add package-lock.json files as this will be the locked npm package versions file - updated activity_list.provider tag to include FQCN for the activity 178037210 - Moved 'old' customerAddressMap template and placeholder from CustomerBundle to generic addressMap in the AddressBundle for reusability - Removed old template and placeholder of customerAddressMap - Servicepoints map now uses placeholder from the AddressBundle instead of embedded implementation 82bee64d5 - Moved 'old' customerAddressMap template and placeholder from CustomerBundle to generic addressMap in the AddressBundle for reusability - Removed old template and placeholder of customerAddressMap - Servicepoints map now uses placeholder from the AddressBundle instead of embedded implementation 078492453 MAR10001-776: Inventory Batches - updated Inventory Level import/export - added column 'enabled batch inventory' to inventory items grid fc8c4387e MAR10001-776: Inventory Batches - updated Inventory Level import/export - added column 'enabled batch inventory' to inventory items grid a56959684 MAR10001-776: Inventory Batches - updated Inventory Level import/export - added column 'enabled batch inventory' to inventory items grid 3ee315d59 Merge branch 'feature/CRH19001-18_Service-points' into feature/update-to-rc3 02918fd66 Merge branch 'feature/CRH19001-18_Service-points' into feature/update-to-rc3 eadf26e02 - updated invoice email attachment template to match the subject of the invoice notification email 653dd6fd1 - updated invoice email attachment template to match the subject of the invoice notification email f7fd44771 Merge branch 'feature/update-to-rc3' of https://github.com/marellocommerce/development-mono-repository into feature/update-to-rc3 cc78cdad3 Merge branch 'feature/update-to-rc3' of https://github.com/marellocommerce/development-mono-repository into feature/update-to-rc3 be4c9f2ae - updated SendEmailTemplateAttachmentAction with methods, properties and dependencies in order to make it work with 4.1-rc3 - updated template for invoice email to make it look like the original bae24cb56 - updated SendEmailTemplateAttachmentAction with methods, properties and dependencies in order to make it work with 4.1-rc3 - updated template for invoice email to make it look like the original 6392d133a - updated pdf labels and headers for invoice lines in pdf - added some css tweaks to pdf, smaller font size, line color changes - split the description in the invoice line item of pdf into productSku and productName and have separate columns for it in the pdf 66c0f54c9 - updated pdf labels and headers for invoice lines in pdf - added some css tweaks to pdf, smaller font size, line color changes - split the description in the invoice line item of pdf into productSku and productName and have separate columns for it in the pdf 02ebfdd35 - updated customer update view to correctly display name in the tab of the browser 3c50453ed - updated customer update view to correctly display name in the tab of the browser 3ea3e88c1 - removed old bundle of Servicepoints, Servicepoints are now a separate package a727ae292 - removed old bundle of Servicepoints, Servicepoints are now a separate package 44f4cf6e3 - added service points as new package - added docker-compose file for service points application a79cd78f4 - added service points as new package - added docker-compose file for service points application dade4f5e3 - updated dev lock files to include mtdowling/cron-expression version v1.2.3 to cover for the incorrect cron expression of the oro:cron:calendar:date in the OroReportBundle 4412c5601 - fixed js component for purchase orders - fixed issue with showing currency symbol and prices cb5eb34f3 - fixed js component for purchase orders - fixed issue with showing currency symbol and prices 2eb5b91dc - fixed js component for purchase orders - fixed issue with showing currency symbol and prices a0a9af782 - fix controller - fix view 284cff7f4 - fix controller - fix view b02106eb7 - fixed create return button on order view page after the status of items have been changed to enums instead of strings e349ca4ee - fixed create return button on order view page after the status of items have been changed to enums instead of strings 576071659 - fixed order of import for multiple entity.js of purchase order d0b13e21b - fixed order of import for multiple entity.js of purchase order 7a5a1caa0 - fixed missing nodeje module on purchase order multiple entities - fixed importing issue in the multiple entity of backbone 2cc838df3 - fixed missing nodeje module on purchase order multiple entities - fixed importing issue in the multiple entity of backbone 1d523cffc - reworked the multiple entities setup for purchase orders regarding js components deeb6eb62 - reworked the multiple entities setup for purchase orders regarding js components 7286d3fd8 - fixed typo in item view of purchase orders 4cba9e4ab - fixed typo in item view of purchase orders cdd73bc3a CRH19001-18; Change date formtype 5d2a982b6 CRH19001-18; Change date formtype 6b3b24e03 CRH19001-18; Refactor form handler for business hours overrides 23f871f7b CRH19001-18; Refactor form handler for business hours overrides 59e718e52 - expose additional js files for purchase order bundle - added some phpdoc to the LogoProvider - removed unnecessary dependency from LogoProvider in pdf bundle 3bd28bf9e - expose additional js files for purchase order bundle - added some phpdoc to the LogoProvider - removed unnecessary dependency from LogoProvider in pdf bundle 03059ddff CRH19001-18; Fix business hours overrides datagrid 4e7b92b28 CRH19001-18; Fix business hours overrides datagrid 2f04f513b - fixed issue with logo showing X as image when no image is found in the invoice - updated check, to check against the common parent of the Invoice & Creditmemo, the AbstractInvoice to make it work for both Invoice and Creditmemo bb9c7a7c8 - fixed issue with logo showing X as image when no image is found in the invoice - updated check, to check against the common parent of the Invoice & Creditmemo, the AbstractInvoice to make it work for both Invoice and Creditmemo 5f4973652 - fixed adding return items to return when creating it from the datagrid, status is now an enum and needs the id as identifier for checking the status instead of the object - make the the business rule mananager service public in order to use it in the process of authorizing the return items b37f81d7a - fixed adding return items to return when creating it from the datagrid, status is now an enum and needs the id as identifier for checking the status instead of the object - make the the business rule mananager service public in order to use it in the process of authorizing the return items dc05e390f - fixed issue with wrong service definitions in the pdf bundle in order to download invoice pdf's - fixed service definition of saleschannel configuration form 6041ea2c0 - fixed issue with wrong service definitions in the pdf bundle in order to download invoice pdf's - fixed service definition of saleschannel configuration form 5e3ed72c5 Merge branch 'feature/MAR10001-776' into feature/update-to-rc3 59dfd625a Merge branch 'feature/MAR10001-776' into feature/update-to-rc3 66777f0da - updated emailtemplate action to comply with php 7.2.x 316f99b67 - updated emailtemplate action to comply with php 7.2.x 2365984dc - make service public in order to use the service within symfony 4.4.x framework on other places ebc1ae9ef - make service public in order to use the service within symfony 4.4.x framework on other places 310222f69 Merge branch 'feature/MAR10001-13_Create-pdfs' into feature/update-to-rc3 192df4cb8 Merge branch 'feature/MAR10001-13_Create-pdfs' into feature/update-to-rc3 a88d455e1 Merge branch 'feature/MAR10001-742_Send-invoice-in-workflow' into feature/update-subscriptions-rc3 a8659a27c Merge branch 'feature/MAR10001-742_Send-invoice-in-workflow' into feature/update-subscriptions-rc3 99b600615 Merge branch 'feature/MAR10001-742_Send-invoice-in-workflow' into feature/update-subscriptions-rc3 7c0f196f7 Merge branch 'feature/MAR10001-742_Send-invoice-in-workflow' into feature/update-subscriptions-rc3 8d4dee8de merged branch 3.0 into feature/MAR10001-776 9bd950cc3 merged branch 3.0 into feature/MAR10001-776 a1a938d99 merged branch 3.0 into feature/MAR10001-776 595bc3a20 MAR10001-776: InventoryBatches b27a9ae8c MAR10001-776: InventoryBatches 19b0a4aa8 Merge branch 'feature/MAR10001-776' into feature/update-subscriptions-rc3 6e60193a9 Merge branch 'feature/MAR10001-776' into feature/update-subscriptions-rc3 5733ea805 Merge branch 'feature/MAR10001-776' into feature/update-subscriptions-rc3 7b5245e71 Merge branch 'feature/MAR10001-805' into feature/update-subscriptions-rc3 20233da2c Merge branch 'feature/MAR10001-805' into feature/update-subscriptions-rc3 eb894e455 - Fixed install issue of Subscription with attribute set not having an organization property anymore - Updated dev.lock files of all applications to RC3 9b442c953 - Fixed install issue of Subscription with attribute set not having an organization property anymore - Updated dev.lock files of all applications to RC3 905110932 - Fixed install issue of Subscription with attribute set not having an organization property anymore - Updated dev.lock files of all applications to RC3 efee8011c CRH19001-18; Add business hours overrides 923142625 CRH19001-18; Add business hours overrides e94354bbc - updated address js component to use the widgetmanager with the updated structure - updated address related views for Order, Supplier and Subscription b7db90238 - updated address js component to use the widgetmanager with the updated structure - updated address related views for Order, Supplier and Subscription 9620c2ec6 - updated address js component to use the widgetmanager with the updated structure - updated address related views for Order, Supplier and Subscription bfa5931f9 CRH19001-18; Manage collections of business hours in form handler 231bb0e94 CRH19001-18; Manage collections of business hours in form handler e6b9a080c CRH19001-18; Always show all days in form b380c5ad4 CRH19001-18; Always show all days in form 1f3e58698 CRH19001-18; Fix validation 506badde8 CRH19001-18; Fix validation 23ad02620 - fix first part of the OrderReplenishment JS module - [WIP]: bug when deleting a single row and adding it back again, in which it is rendered but not in the collection of added products 9d6bad5c8 - fix first part of the OrderReplenishment JS module - [WIP]: bug when deleting a single row and adding it back again, in which it is rendered but not in the collection of added products 438cd4074 - fix first part of the OrderReplenishment JS module - [WIP]: bug when deleting a single row and adding it back again, in which it is rendered but not in the collection of added products 6051c0692 CRH19001-18; Rewrite business hours datastructure [WIP] 9c20c1588 CRH19001-18; Rewrite business hours datastructure [WIP] 9be307c6c CRH19001-18; Rewrite business hours datastructure [WIP] 1fabb3439 CRH19001-18; Rewrite business hours datastructure [WIP] 0fde8de36 CRH19001-18; Rewrite business hours datastructure [WIP] 6f40bb129 CRH19001-18; Rewrite business hours datastructure [WIP] 4bb000f02 - updated dev.lock of ce to use 4.1-rc2 - updated enterprise application to include new requirements and updated config by removing require_js config settings f0fd1db5e CRH19001-18; Update form view for business hours c8b5fe623 CRH19001-18; Update form view for business hours 38575d13d CRH1900-18; Fix validation 2da004692 CRH1900-18; Fix validation f25a67557 Merge branch 'feature/CRH19001-18_Service-points' of github.com:marellocommerce/development-mono-repository into feature/CRH19001-18_Service-points 3bac8acd8 Merge branch 'feature/CRH19001-18_Service-points' of github.com:marellocommerce/development-mono-repository into feature/CRH19001-18_Service-points 682c99c9d CRH19001-18; Updates to form theme 95c9fdc18 CRH19001-18; Updates to form theme 4b9c8c683 - Updated js resources for orocommerce bridge, enterprise and subscription to comply with new webpack implementation b53c39a0a - Updated js resources for orocommerce bridge, enterprise and subscription to comply with new webpack implementation 28daddedd - updated dev.lock file fe90bbaa2 - updated all Marello CE bundles with the new format for webpack 5193d734f - updated all Marello CE bundles with the new format for webpack 604d4d97b CRH19001-18; Fix view page when no image selected 5cd88e9dd CRH19001-18; Fix view page when no image selected f6da13472 - updated Product related js components to comply with new webpack setup - Updated EmailTemplateManager for getting the localized email template were the old method has been removed c252f838e - updated Product related js components to comply with new webpack setup - Updated EmailTemplateManager for getting the localized email template were the old method has been removed 31156333b - Updated application to Oro Platform 4.1-RC - Updated some components to comply with new webpack implementation of Oro Platform 4.1-rc 227d2865f - Updated application to Oro Platform 4.1-RC - Updated some components to comply with new webpack implementation of Oro Platform 4.1-rc 3fa848d95 - Updated application to Oro Platform 4.1-RC - Updated some components to comply with new webpack implementation of Oro Platform 4.1-rc 83969e824 - Updated application to Oro Platform 4.1-RC - Updated some components to comply with new webpack implementation of Oro Platform 4.1-rc 669d346e0 CRH19001-18; Add custom form theme for service point business hours 75290c1b1 CRH19001-18; Add custom form theme for service point business hours d6bb3b9b1 MAR10001-776: Inventory Batches - new comments fixed 6200983d2 MAR10001-776: Inventory Batches - new comments fixed 924d920e1 MAR10001-776: Inventory Batches - new comments fixed 4f8d33afc CRH19001-18; Fix timezones of TimePeriod entities 0cbb51dec CRH19001-18; Fix timezones of TimePeriod entities 1e5841ce9 MAR10001-805: Error exporting inventory (and 'Download data template') - Error exporting inventory (and 'Download data template') - Change default text 'Oro' to 'Marello' in email Sender Name b6c984d5d MAR10001-805: Error exporting inventory (and 'Download data template') - Error exporting inventory (and 'Download data template') - Change default text 'Oro' to 'Marello' in email Sender Name b5d0795ce MAR10001-742; Add ability to add attachments to notifications from workflow action 6eb9d1569 MAR10001-742; Add ability to add attachments to notifications from workflow action 737fc682d MAR10001-742; Add ability to add attachments to notifications via SendProcessor 3c9be9991 MAR10001-742; Add ability to add attachments to notifications via SendProcessor 10b411531 MAR10001-742; Add attachments to Notification 6bc8305a6 MAR10001-742; Add attachments to Notification e4479f701 MAR10001-742; Add v1_0 migration for NotificationBundle a5dff3ee3 MAR10001-742; Add v1_0 migration for NotificationBundle 0ec555db6 MAR10001-742; Fix typo 8fa0f8f00 MAR10001-742; Fix typo 78454b743 - reverted to old sticky note icon 6525fe1e0 - reverted to old sticky note icon 9c7cff9d3 - changed the sticky note text - reverted commmented line in phpunit.xml 25ed67191 - changed the sticky note text - reverted commmented line in phpunit.xml a5d99b495 - fixed the sticky note description and logo - updated help link on the question icon on the top 106395942 - fixed the sticky note description and logo - updated help link on the question icon on the top 87cc5f9b7 MAR10001-13; Add white background color to PDF template c5ddc1a7e MAR10001-13; Add white background color to PDF template 895b6a11c MAR10001-13; Add configuration button on sales channel view f4f6289f4 MAR10001-13; Add configuration button on sales channel view 27c15c12d CRH19001-18; Validate timeperiod validity 598944c8e CRH19001-18; Validate timeperiod validity 8dc19290d CRH19001-18; Allow multiple timeperiods per day df4565703 CRH19001-18; Allow multiple timeperiods per day 7bdd0349d - remove instore assistant bundle as we are going to move the code to a separate repo and introduce it as an extension 985343a46 MAR10001-774: Remove increase / decrease external warehouses option 8c74043b3 MAR10001-774: Remove increase / decrease external warehouses option 740c4e7a1 MAR10001-774: Remove increase / decrease external warehouses option c4b99caec - CS fix 3c68ee563 - CS fix 58962e2b5 Merge branch 'feature/MAR10001-765' into 3.0 05cd663b2 Merge branch 'feature/MAR10001-765' into 3.0 1294fd60d Merge branch 'feature/MAR10001-765' into 3.0 83a15435f Merge branch '3.0' into feature/MAR10001-776 32dd31b4b Merge branch '3.0' into feature/MAR10001-776 3164fd5e1 Merge branch '3.0' into feature/MAR10001-776 b0a779dde MAR10001-802: WFA rule index is not rendering 0991d23c1 MAR10001-802: WFA rule index is not rendering 4766df7ec MAR10001-802: WFA rule index is not rendering 763b003ce MAR10001-765: Update InventoryRebalance Command e88a5d820 MAR10001-765: Update InventoryRebalance Command e9ec53480 MAR10001-765: Update InventoryRebalance Command 26a7c3c84 - updated symfony/contracts to 1.1.0 instead of 1.0.2 to be more up to date with dependency version and not have it break the installation with the dev.json 235ebf9e9 - updated dev.lock files to include symfony/contracts v1.0.2 since it fails to install on some servers with v1.1.8 :/ ae3bb88eb - updated to 4.1-beta on marello-ce b6326501a MAR10001-776: Inventory Batches - bug fixed a8b947cfa MAR10001-776: Inventory Batches - bug fixed a21503972 MAR10001-776: Inventory Batches - bug fixed 2a7b9beb8 MAR10001-776: Inventory Batches - bug fixed 690e5d913 MAR10001-776: Inventory Batches - bug fixed 76518c5c6 MAR10001-776: Inventory Batches - added inventoryBatch creation on PurchaseOrder complete - updated inventoryBatches allocation by q-ty for order fe1066805 MAR10001-776: Inventory Batches - added inventoryBatch creation on PurchaseOrder complete - updated inventoryBatches allocation by q-ty for order 05feb9490 MAR10001-776: Inventory Batches - added inventoryBatch creation on PurchaseOrder complete - updated inventoryBatches allocation by q-ty for order 46c7b18ce - Added more fixes regarding the changed DI in the Controller as the service locator is more limited than the 'normal' one - Aliased classes to service ids in order to get the subscribed services correctly in the Controller dd2603baf - Added more fixes regarding the changed DI in the Controller as the service locator is more limited than the 'normal' one - Aliased classes to service ids in order to get the subscribed services correctly in the Controller c51753d46 - Added OrderDashboardController as a service tagged with the container.service_subscriber in order to fix issues with private dependencies instead of using regular DI - Fixed issue with private service which provides data to a dashboard widget cae7edad7 - Added OrderDashboardController as a service tagged with the container.service_subscriber in order to fix issues with private dependencies instead of using regular DI - Fixed issue with private service which provides data to a dashboard widget 7f394a308 Merge branch 'feature/MAR10001-770' into 3.0 d1b98eb90 Merge branch 'feature/MAR10001-770' into 3.0 dcb7aa4a4 Merge branch 'feature/MAR10001-775' into maintenance/2.2 9fe1a4e14 Merge branch 'feature/MAR10001-775' into 3.0 cd9313e3c Merge branch 'feature/MAR10001-775' into 3.0 619e6fd78 Merge branch 'feature/MAR10001-769' into 3.0 d4a649819 Merge branch 'feature/MAR10001-769' into 3.0 02a53141e Merge branch 'feature/MAR10001-769' into 3.0 d14bcb338 - updated dev.lock with Oro Platform beta 4.1 217d280ea - updated dev.lock with Oro Platform beta 4.1 30ef262ad - updated dev.lock with Oro Platform beta 4.1 6fa5a778a MAR10001-776: Inventory Batches - removed temp code 917cdc829 MAR10001-776: Inventory Batches - removed temp code b95c8380c MAR10001-776: Inventory Batches 40019aaf9 MAR10001-776: Inventory Batches e6e150fc7 MAR10001-776: Inventory Batches 0647a3566 - added SymfonyRequirements to ee environment - [WIP]: updated assignSalesChannels.twig, still not able to mass assign saleschannels to products - updated order view to switch invoices and packingslip grids as it seems a more logical order for displaying the data - removed unnecessary divs which where messing up the css on some datagrids a484d5598 - added SymfonyRequirements to ee environment - [WIP]: updated assignSalesChannels.twig, still not able to mass assign saleschannels to products - updated order view to switch invoices and packingslip grids as it seems a more logical order for displaying the data - removed unnecessary divs which where messing up the css on some datagrids 86e648a01 - Fixed issue with deprecated dependency of OrderWarehousesProvider where the service definition was updated but not the class itself - Updated dev.lock file for Marello CE to include Oro 4.x - Added SymfonyRequirements to git as this is not being installed via composer anymore - Updated phpunit.xml.dist by removing incompatible notation of logging incomplete tests - Updated composer.json and dev.json files to include symfony require as extra to use symfony flex globally c22d03f9a - Fixed issue with deprecated dependency of OrderWarehousesProvider where the service definition was updated but not the class itself - Updated dev.lock file for Marello CE to include Oro 4.x - Added SymfonyRequirements to git as this is not being installed via composer anymore - Updated phpunit.xml.dist by removing incompatible notation of logging incomplete tests - Updated composer.json and dev.json files to include symfony require as extra to use symfony flex globally 63a48116c - Fixed issue with deprecated dependency of OrderWarehousesProvider where the service definition was updated but not the class itself - Updated dev.lock file for Marello CE to include Oro 4.x - Added SymfonyRequirements to git as this is not being installed via composer anymore - Updated phpunit.xml.dist by removing incompatible notation of logging incomplete tests - Updated composer.json and dev.json files to include symfony require as extra to use symfony flex globally 13dcc1f9d - updated oro platform version to 3.1.15 - added script to enable / disable xdebug, can only be used when logged in as root in the container 0fb3f43b4 - updated dev.lock - updated environment to look for version 4 instead of 3 when starting the consumer 467677402 Merge branch '3.0' into feature/MAR10001-770 6fccd993c Merge branch '3.0' into feature/MAR10001-770 f421f1d23 fixed bug on order create page 21589a0bd fixed bug on order create page 90dc5f64c MAR10001-770: Order statistics widget: filter by SalesChannel 1ff5a6164 MAR10001-770: Order statistics widget: filter by SalesChannel cf4217608 - Added migration to include organization id on the purchase order item 68f88547c - Added data column to PurchaseOrder f543a3824 MAR10001-775: Product widget failed to load when creating order without selecting a SalesChannel first 10cafed1c MAR10001-775: Product widget failed to load when creating order without selecting a SalesChannel first 098bc5146 Merge branch 'develop' into feature/MAR10001-769 a34c4d6af Merge branch 'develop' into feature/MAR10001-769 41e354923 Merge branch 'feature/MAR10001-758' into develop ae693ab4c Merge branch 'feature/MAR10001-758' into develop d60f3a7f3 Merge branch 'feature/MAR10001-758' into maintenance/2.2 9f2cf4c54 - CS fixes 2ab467421 - CS fixes 0ef03ef55 - updated branch aliases f8049b093 Merge commit '01a0d5ae6603999e572ad65466187b50d8cba388' into maintenance/2.2 5f1757ce5 - CS fixes - Changed php version back to 7.1 face93ac8 CRH19001-18; Add API 92722bccf CRH19001-18; Add API b832b1717 CRH19001-18; Add map to service point view page 02939ef0b CRH19001-18; Add map to service point view page 23e5e4e6c CRH19001-18; Add service point image to view 4eadfa473 CRH19001-18; Add service point image to view 8416988bc CRH19001-18; Add EntityConfig 9c1030a21 CRH19001-18; Add EntityConfig 5033f1ccd CRH19001-18; Add ACL 3ca3135f4 CRH19001-18; Add ACL 6d9054f9a CRH19001-18; Add controllers and views 0cfb53c59 CRH19001-18; Add controllers and views a34157713 - updated oro platform version to 3.1.15 - added script to enable / disable xdebug, can only be used when logged in as root in the container c0cb485b9 MAR10001-758: ProductSupplierRelation deletion is not updating InventoryLevels ceaf461e2 MAR10001-758: ProductSupplierRelation deletion is not updating InventoryLevels 4aa5ba4b5 Merge branch 'develop' into feature/MAR10001-758 4e9b55324 Merge branch 'develop' into feature/MAR10001-758 afa0bbcfc MAR10001-769: Show 'Cost' currency on de Product edit page 62c11fd93 MAR10001-769: Show 'Cost' currency on de Product edit page 79ae658dc CRH19001-18; Add Forms 3e78149c1 CRH19001-18; Add Forms b9c3c3257 CRH19001-18; Add validation b630b8cd7 CRH19001-18; Add validation 2d4e1ff7a CRH19001-18; Add translations 07a769e0f CRH19001-18; Add translations 7e32b8dfb CRH19001-18; Add data model 292a8e349 CRH19001-18; Add data model 763fa3517 CRH19001-18; Add ServicePointBundle 15a36f12b CRH19001-18; Add ServicePointBundle 01a0d5ae6 - removed self.version from package composer.json files 50883c5da - Changed column source on invoice and creditmemo items product sku and name d337d0963 Merge branch 'feature/MAR10001-674' into develop e7dae6c0e - Disabled OrderOnDemand widget in Inventory view / update in order to prevent the usage of the unfinished feauture 6b78c8848 Merge branch 'feature/MAR10001-674' into develop eab1858f1 Merge branch 'bugfix/MAR10001-764-stop-override-orderitem-status-when-already-set' into develop 104c43817 - Added check to prevent overriding OrderItemStatus when already set on creation fe8bb42ba Merge branch 'bugfix/MAR10001-763-fix-inventory-level-import' into develop 1c6d80544 - Added additional check to verify there is a product to get the inventory item from when passed back to the import processor 97c9b2fcc - Added additional check to inventory level update strategy to check wether the product of the 'associated' inventory level exists before importing and throwing errors e48fc472c removed useless fields from oro_entity_config_field 465492e30 removed useless fields from oro_entity_config_field 9a7010469 fixed bug in Migrations versions 813bbd944 fixed bug in Migrations versions 53ebeb0e7 fixed bug in Migrations versions 3eb689333 fixed bug in Migrations versions 3affb539f fixed bug in OrderBundle migration 5812d0894 fixed bug in OrderBundle migration 24fdddaa0 merged branch develop into 3.0 e043d4722 merged branch develop into 3.0 f84c3c90c merged branch develop into 3.0 fe06756a9 Merge branch 'task/MAR10001-0-add-quick-navigation-in-views' into develop 154bd1592 Merge branch 'task/MAR10001-0-add-quick-navigation-in-views' into maintenance/2.1 4c66dac97 Merge branch 'task/MAR10001-add-orocommerce' into develop add2b43cc merge branch 3.0 into feature/MAR10001-730 05e8bf957 merge branch 3.0 into feature/MAR10001-730 17b92a3dc merge branch 3.0 into feature/MAR10001-730 864b93968 merge branch 3.0 into feature/MAR10001-730 1cce30b4a merge branch 3.0 into feature/MAR10001-714 0b8bdb496 merge branch 3.0 into feature/MAR10001-714 5b9024db8 merge branch 3.0 into feature/MAR10001-714 b75c1ed00 merge branch 3.0 into feature/MAR10001-714 c462f187e MAR10001-717: Update applications with Oro platform 4.0.0 - fixed bug in migrations in InventoryBundle related to renaming column from system to is_system 4c9b5b4bc MAR10001-717: Update applications with Oro platform 4.0.0 - fixed bug in migrations in InventoryBundle related to renaming column from system to is_system ca97bfc4e Merge branch '3.0' into feature/MAR10001-725 09421c85e Merge branch '3.0' into feature/MAR10001-725 ceeafb61f Merge branch '3.0' into feature/MAR10001-725 931f1932c Merge pull request #16 from marellocommerce/feature/MAR10001-751 989b3f2c9 Merge pull request #16 from marellocommerce/feature/MAR10001-751 56e5d5d61 Merge branch '3.0' into feature/MAR10001-718 8a15bf5a1 Merge branch '3.0' into feature/MAR10001-718 c790edaa0 - finetuned composer json files for oro commerce 4ddc0c617 Merge branch 'task/MAR10001-745-add-organization-ownership-to-child-entities' into develop 6f402e9ca - updated PurchaseOrderBundle schema versions to take the 'next' version of Marello (2.2) into account 6c6ae2d34 Merge branch 'task/MAR10001-745-add-organization-ownership-to-child-entities' into develop df0124130 Merge branch 'task/MAR10001-745-add-organization-ownership-to-child-entities' into develop 52a78660f - Added Organization ownership to ReplenishmentOrderItem - Added migration to update current ReplenishmentOrderItems - Added translation for Organization label - Updated installer migration to include Organization column 7c1ba3419 - CS fix dd6a7b011 - Added Organization ownership to PurchaseOrderItem - Added translation for Organization label on PurchaseOrderItem - Added migration to update current PurchaseOrderItem with Organization - Added migration to add column for ownership to PurchaseOrderItem table - Added translation for Organization label on ReturnItem e53eefe14 - Added translations for Return item and Refund item organization labels - Added Organization ownership to RefundItem - Added migrations to update current RefundItems with Organization - Added migrations to add ogranization colum to RefundItem table 0fb228023 - Added migration for updating existing InvoiceItems with Organization - Added migration for adding ownership to ReturnItems - Added migration for updating existing ReturnItems with Organization 92e27ecf5 MAR10001-751: Rename columns to bbdd49b9e MAR10001-751: Rename columns to 893c103da MAR10001-717: Update applications with Oro platform 4.0.0 - fixed bugs a50481065 MAR10001-717: Update applications with Oro platform 4.0.0 - fixed bugs 1491c5475 MAR10001-717: Update applications with Oro platform 4.0.0 - fixed CustomerBundle - fixed PaymentTermBundle f439b84c9 MAR10001-717: Update applications with Oro platform 4.0.0 - fixed CustomerBundle - fixed PaymentTermBundle 8612c07ae merge develop branch into feature/MAR10001-717 ' a885563af merge develop branch into feature/MAR10001-717 ' eb6dc9d3e merge develop branch into feature/MAR10001-717 ' 84443da43 merge develop branch into feature/MAR10001-717 ' 4fabaabf6 - Added migration to update current credit/invoice items with organization based on the parent entity's organization 7a30b978e MAR10001-758: ProductSupplierRelation deletion is not updating InventoryLevels 2c93997a1 MAR10001-758: ProductSupplierRelation deletion is not updating InventoryLevels fd600cf5e - updated PackingSlipItem test to include organization - Added translation for PackingSlipItem organization 86a679149 - Added migrations for updating current OrderItems & PackingSlipItems to set the organization based of their parent entity - Added Ownership config to PackingSlipItem - Added migration to add the new organization column on PackingSlipItem c45b7e495 - Added ownership field to Oro config 938fcb2e5 - Added organization ownership to InvoiceItems - Added translations for organization - Added migration to update schema with organization 2e7dcce2f - Added organization ownership to OrderItem for applying ACL when reports are created with the items 03055ef1f - Added Organization ownership to Supplier entity - Added test to verify the organization is set when a new supplier is being created cd439fbfd MAR10001-674: Order on demand - updated OOD validator - fixed bugs in order items statuses changing eccc3fc5c - updated rendering of OrderNumber link and SalesChannel link on various views for quick navigation - added quick navigation link to customer on Invoice view ee1c715a1 - Added product sku rendering link to RefundItem's datagrid for quick navigation 9b77d2459 - Added product SKU rendering link to ReturnItems grid for quick navigation b8a832823 - Added product sku rendering link to PackingSlip item grid for quick navigation ace617480 - Added product sku link rendering to Invoice Item grid for quick navigation a327b1fdb - Added product sku link rendering to order item's product in order item datagrid 2d2303bf7 - Added quick navigation rendering link for product SKU's as macro and template for Twig templates - Added quick navigation product sku link to datagrids of Inventory related datagrids - Updated rendering product sku link in inventory view pages - Updated rendering product sku link in order's hero widget 0e2cbe4f4 - Updated rendering of orderNumber in datagrid to check ACL access and handle rendering of both an id and object of order - Added rendering a view link to latest order grid in dashboard widget for quick navigation 048587cf3 - Added quick navigation for customer on Order View - Updated quick navigation for SalesChannel link on Order View 4ba6fd268 MAR10001-714: Payment methods 54f3f9f07 MAR10001-714: Payment methods 38f1adb17 MAR10001-714: Payment methods 6c1fc09a5 merge develop branch into feature/MAR10001-714 54fe24b3f Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow 7c2e68e36 Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow 9c966430d Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow 4b750e19c Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow 9c172db5e - updated lock files for php 7.1.x since this is the minimum installation req for php - added some args to build context of the web container for changing php versions 'easier' 0f8d89e19 MAR10001-742; Add tests 40c061fa7 MAR10001-742; Add tests f8d1a0d3f MAR10001-742; Check if invoice workflow transition is set e455f1228 MAR10001-742; Check if invoice workflow transition is set 8ea852060 MAR10001-742; Remove debug code 30fd7e03c MAR10001-742; Remove debug code 9e774ebfe MAR10001-742; Add workflow actions to send invoice e-mail 503189527 MAR10001-742; Add workflow actions to send invoice e-mail df25693a0 MAR10001-742; Add translation e953097f1 MAR10001-742; Add translation de8382e18 MAR10001-742; Add migration for invoice e-mail template e9bbb0e1d MAR10001-742; Add migration for invoice e-mail template 5e73fe3fb MAR10001-742; Add IsSendEmailTransition workflow condition 3b99d8921 MAR10001-742; Add IsSendEmailTransition workflow condition 123c0434e merged develop branch into feature/MAR10001-714 branch 29bbcad6c merged develop branch into feature/MAR10001-714 branch f4a48dc3d merged develop branch into feature/MAR10001-714 branch fea788d98 merged develop branch into feature/MAR10001-714 branch 716f69fe1 - Reverted changes that break BC, added comments where BC would break if we would keep the changes in the Interface - Added alternatives to provide same functionality but without BC breaks b7c081cb7 - Reverted changes that break BC, added comments where BC would break if we would keep the changes in the Interface - Added alternatives to provide same functionality but without BC breaks 9083c16e5 Merge branch 'develop' into feature/MAR10001-674 42ffd497d - CS fix -> fixed docblock ee037a4d4 Merge branch 'feature/MAR10001-739_Payment-term-invoice' into develop 7798de002 Merge branch 'task/MAR10001-0-remove-unessary-rebalance-job-from-migration' into develop 50f910317 - Added Migrations to update entity_config tables and entity_config_field tables to set auditable to false for InventoryLevel && BalancedInventoryLevel ebd6baf56 Merge branch 'task/MAR10001-0-remove-unessary-rebalance-job-from-migration' into develop 5e3dcf58b Merge branch 'task/MAR10001-0-remove-unessary-rebalance-job-from-migration' into develop 437eb0df5 Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow c26c4cd0a Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow efd9a82bf Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow c0256e204 Merge branch 'develop' into feature/MAR10001-742_Send-invoice-in-workflow 7fb05b270 - removed the trigger of a rebalance when installing to reduce additional messages in queue - refactored the trigger of rebalancing when a SalesChannelGroups are created or deleted to only rebalance products linked to those SalesChannelGroups a546cf3f4 - Added check to prevent sorting on null when no products have been found and linked to any warehouse when creating order and moving through the workflow dc861c587 MAR10001-742; Add e-mail settings about sending invoices ec63c7a76 MAR10001-742; Add e-mail settings about sending invoices 041ed21c0 MAR10001-739; Fix migration namespace 3fad78266 - CS Fixes ce2722b62 - CS Fixes 8696b07e4 Merge branch 'feature/MAR10001-674' of https://github.com/marellocommerce/development-mono-repository into feature/MAR10001-674 d37f12418 MAR10001-674: Order on Demand - fixed warehouses calculations for order fc82a7356 MAR10001-674: Order on Demand - fixed warehouses calculations for order 663149607 - remove unused code from validator e705e58d5 - Removed unused code - Added CS fixes where necessary 756dcea67 MAR10001-674: Order on Demand 23e5e6f89 MAR10001-674: Order on Demand 7e9bb647f MAR10001-674: Order on Demand 3d3bcc350 MAR10001-674: Order on Demand f4683f553 - Updated dev.lock files - Updated oro platform to version 3.1.13 bb5212522 - Updated dev.lock files - Updated oro platform to version 3.1.13 cd1c5f2b0 MAR10001-730: Update CustomerBundle with Customer - rollback in OroRequirements.php 3d17b9064 backmerged develop branch into feature/MAR10001-730 branch 4b57cc36f backmerged develop branch into feature/MAR10001-730 branch 35f2109cc backmerged develop branch into feature/MAR10001-730 branch 608d7956e backmerged develop branch into feature/MAR10001-730 branch f4cc70c09 MAR10001-730: Update CustomerBundle with Customer 80d05c795 MAR10001-730: Update CustomerBundle with Customer 2ca867546 MAR10001-730: Update CustomerBundle with Customer ab4959178 MAR10001-742; Add workflow action to send email with attachment 551422af0 MAR10001-742; Add workflow action to send email with attachment e796ca95c MAR10001-739; Add payment term to invoice view 00e5addd2 MAR10001-739; Calculate invoice due date b68195820 MAR10001-739; Add payment term during invoice generation 376b14521 MAR10001-739; Add getCustomerPaymentTerm to PaymentTermProvider d5f0ff2f9 MAR10001-739; Add payment term to invoice model bbe4f4e27 - platform update 827a74d3d - platform update 58c6d1bb6 MAR10001-13; Add demo data for invoice configuration 86867d3bf MAR10001-13; Add demo data for invoice configuration 9daa545b8 Merge branch 'feature/MAR10001-744_Payment-term-for-Company' into develop e46245ea7 - Fixed typo in class name 4f7dad1f6 Merge branch 'feature/MAR10001-744_Payment-term-for-Company' into develop 91ab98143 - Small CS fixes; new line on end of file, formatting ano function over multiple lines bfcf14bd3 MAR10001-730: Move Customer 17ec80ebc MAR10001-730: Move Customer 55d1c48fc MAR10001-13; Add functional test 197e7ae69 MAR10001-13; Add functional test 1391e4d93 MAR10001-13; Update TableSizeProviderTest 5b3449ea7 MAR10001-13; Update TableSizeProviderTest 56e0d3d01 Merge commit '1fcaceed572eb29296aba81d50c4746ea44a9d0b' into maintenance/2.1 1fcaceed5 Squashed 'applications/marello-application-ee/' changes from d13f2b41e..a69ba4abb 8fd056727 Merge branch 'bugfix/MAR10001-712-add-taxcode-remove-suppliers' into maintenance/2.1 6c8f2edc5 Merge branch 'bugfix/MAR10001-712-add-taxcode-remove-suppliers' into maintenance/2.1 67e652aa8 Merge branch 'bugfix/GH-PR9-update-ororequirements' into develop 52931de98 Merge branch 'bugfix/GH-PR9-update-ororequirements' into develop 3bb3068a2 Merge commit '73de19e5ff6867b2fb523997401df9d818e84235' into bugfix/GH-PR9-update-ororequirements 73de19e5f Squashed 'applications/marello-application/' changes from 3d023f800..3a5a0ae59 3a5a0ae59 Update OroRequirements check to be compatible with Oro Platform 3.1.x 341f1346e - Updated demo data for PO in order to make sure there are actually POitem candiates for creating a PO 69b6d44df - Added test to verify correct registration of cron command - updated code to include correct registration of cron command b5f0b28d7 MAR10001-13; PdfBundle unit tests d42a9ea89 MAR10001-13; PdfBundle unit tests d32de0e5c MAR10001-13; Fix PDF bugs b13f6ec40 MAR10001-13; Fix PDF bugs e2078a5d3 - Updated tests to cover ProductRepository getPoItemCandidates - Updated inventory data fixture for tests to include data for InventoryItems - Added test for cron command to verify sending the notification for PO advise - Updated ProductData fixture to set a preferedsupplier on a product 5057b5dba Merge branch 'feature/MAR10001-676-purchase-order-notification' of https://github.com/marellocommerce/development-mono-repository into feature/MAR10001-676-purchase-order-notification 5aa5e00f1 Merge branch 'feature/MAR10001-676-purchase-order-notification' of https://github.com/marellocommerce/development-mono-repository into feature/MAR10001-676-purchase-order-notification 264e42ee4 - Added test to verify the working of the saving of the notification as activity target b4379a611 - updated phpdocblock removing parameter that isn't there anymore 3cb6d4388 - updated query for including purchaseInventory when selecting purchaseOrderItemCandidates 8a2d6dea1 - Updated SendProcessor to keep BC by having a setter and property for saving the notification as activity instead of adding a method parameter - Updated POACommand to set the property on the SendProcessor and use a constant as exit code 35f19c20c Merge branch 'develop' into feature/MAR10001-676-purchase-order-notification bede0276e Merge branch 'develop' into feature/MAR10001-676-purchase-order-notification 3e665b14d Merge branch 'develop' into feature/MAR10001-676-purchase-order-notification 24c7cd59f Merge branch 'develop' into feature/MAR10001-725 0de46972c Merge branch 'develop' into feature/MAR10001-725 4d3d9e317 Merge branch 'develop' into feature/MAR10001-725 ddbba0501 Merge branch 'develop' into feature/MAR10001-725 52c04f91b Merge branch 'develop' into feature/MAR10001-676-purchase-order-notification eb83373e3 Merge branch 'develop' into feature/MAR10001-676-purchase-order-notification 0771a6260 MAR10001-13; Add PDF button to invoice view a8b976bd0 MAR10001-13; Add PDF button to invoice view b3dd7d229 - changed Warehouse Location to Pick location - Updated migrations, tests and translations 286638244 - changed Warehouse Location to Pick location - Updated migrations, tests and translations 17f278515 MAR10001-725: Update ProductBundle 4442a0209 MAR10001-725: Update ProductBundle c38c2ba89 MAR10001-725: Update ProductBundle a093e74d5 MAR10001-744; PaymentTerm API tests d6eebd000 Merge branch 'feature/MAR10001-735-add-second-workflow' into develop f3c4e586b - Added an Interface to have some constants for order workflow names - Updated tests being more clean without the much duplicated code d9e9b7ac1 MAR10001-744; Adjust tests for Company PaymentTerm a38ce3001 MAR10001-744; Add PaymentTerm API d402c756a MAR10001-744; Add payment term to Company view and form 2590eddfb MAR10001-744; Add test for PaymentTermSelectType 754cc9a3d MAR10001-744; Add PaymentTermSelectType 95a00873f - Updated logic for moving default worfklows to pending when only one is active - Added test for logic to test scenario's where there is only a single one available, the entity not being eligible or when there is more than one available - Added tests to interact with default installation 0d3126a61 MAR10001-744; Add payment term to Company model a329c447b - Added first version of the OrderWorkflowStartListener for when a single workflow is active on the order and start it automatically e59e9b338 - Fix inventory level tests for CE version 634e09c8d Merge branch 'task/MAR10001-0-fix-inventory-level-functional-tests' into develop 314adb7ee - Added warehouseLocation to InventoryLevel form fields 22b572a3b - updated type hint for getWarehouseLocation() in InventoryLevel - Updated datagrid of inventory level to include warehouseLocation - Updated inventory level collection form to include warehouseLocation 4184fa6bd - added warehouseLocation to inventory level form type 3699d44e1 - Added Warehouselocation to InventoryLevel - Updated migrations to include warehouse location column - Added translation for new field warehouse location - Updated function/unit tests to test setting/getting and updating warehouse location 783e12d1f - Fix inventory level tests for CE version c4cb25498 - Fix inventory level tests for CE version e18bc681b - added currency of root entity(Invoice) for formatting correctly in PDF's f93f7750f - added currency of root entity(Invoice) for formatting correctly in PDF's 4c6ea2d12 Merge branch 'feature/MAR10001-701_Payment-term' into develop 5d534a859 - Fixed check in PaymentTermDeletePermissionProvider to take into account a null result from the PaymentTermProvider - Fixed PaymentTermControllerTest to count the result of the loaded fixture and verify 'results' d23d027e4 - Show inventory level collection when empty for CE version 0a3cd1041 - Adding PaymentTerm demo data - Updated payment term provider to check wether the id from the config exists before querying on the repository ffb78a397 - CS fixes db013471c - removed unsued and duplicate class 37bfb8d47 Merge commit '88a66e97085d3973c1f3ab5688d657f589675912' into feature/MAR10001-674 57b78f692 Merge commit '88a66e97085d3973c1f3ab5688d657f589675912' into feature/MAR10001-674 9719849ac Merge commit 'c5439ea9dcce676be7d610bd46086d4843c58fad' into feature/MAR10001-674 bada48fe5 Merge commit 'c5439ea9dcce676be7d610bd46086d4843c58fad' into feature/MAR10001-674 8a83a9d9e Merge commit '22b754da8f9fb0b396a651fd3f9a1a84ad4a6e61' into feature/MAR10001-13_Create-pdfs 498e2c90e Merge commit '22b754da8f9fb0b396a651fd3f9a1a84ad4a6e61' into feature/MAR10001-13_Create-pdfs d004d1a4b Merge commit '22b754da8f9fb0b396a651fd3f9a1a84ad4a6e61' into feature/MAR10001-13_Create-pdfs 048ca9bcf Merge commit '22b754da8f9fb0b396a651fd3f9a1a84ad4a6e61' into feature/MAR10001-13_Create-pdfs e7d8729f8 - updated lock files to have mpdf as dependency in lock files 13f02419a - updated lock files to have mpdf as dependency in lock files 883055c06 Merge commit '9fa49926a0e01508f988164f88559744622be764' into feature/MAR10001-701_Payment-term 48357abe8 MAR10001-718: Refactor LocaleBundle - backmerged develop branch c2b4a349b MAR10001-718: Refactor LocaleBundle - backmerged develop branch 19f681f6a MAR10001-717: Update applications with Oro platform 4.0.0 b4831a27b MAR10001-717: Update applications with Oro platform 4.0.0 e786173fd MAR10001-718: Refactor LocaleBundle ba992802e MAR10001-718: Refactor LocaleBundle ccddf39bc - updated workflow #2 for switching shipping and invoicing steps in order workflow - updated translation for workflow #2 - updated configuration for workflow #1 to be able to handle more than 1 workflow on a single entity - disabled workflow #2 by default 5ad9bcc47 MAR10001-717: Update applications with Oro platform 4.0.0 837111345 MAR10001-717: Update applications with Oro platform 4.0.0 4cab27945 MAR10001-717: Update applications with Oro platform 4.0.0 162e4a2e8 MAR10001-717: Update applications with Oro platform 4.0.0 c424e39c2 Merge branch 'develop' of https://github.com/marellocommerce/development-mono-repository into develop 13543c6a5 - removed unused code - todo add additional customer controller tests for create/update 0d959d084 - removed commented code 09eb13a96 - fixed testCompanyUpdateRemoveCustomers test now calling correct method for customer to assert - removed an assertion from testCompanyUpdateRemoveCustomers because assumption of the assertion was wrong c8c93f122 - CS Fixes - Updated Company controller test to test saving a company without customers assigned - Fixed Customer entity to allow the company to be empty - Added Company name and view link to Order view 58b76bdb0 - CS fixes 0c7035e9b Merge commit '11f7d8417a07b4a65cd787387c0e10cc56870352' into feature/MAR10001-692 deea8a109 - removed suppliers from product grid - added missing taxcode to product grid 0ac39ed58 Merge branch 'bugfix/MAR10001-711-images-load-slower-than-marello-20' into maintenance/2.1 fd8954fd9 - added addditional migration to update product images with `acl_protected` false in Entity config to change the caching directory of images - updated installer to include the `acl_protected` entity config by default in Product Image attachments 22b754da8 MAR10001-13; Add TableSizeProvider 7551ac4f9 MAR10001-13; Adjust template paper size to configuration abe50557f MAR10001-13; Add paper size configuration value 7481e5d9b MAR10001-13; Fix configuration validation 19d2f4cf4 - updated OrderView datagrid layouts to resemble the 'original' style from Oro 2a16c3e34 - remove lock files b53f12c7b - remove lock files from master 3637f5469 - Release preparation 2.1 68465c418 Merge commit 'ce6c5c85565cf4ba66d2942b1c1aa34c3c389a33' into release/2.1 f92c46681 Merge commit 'ce6c5c85565cf4ba66d2942b1c1aa34c3c389a33' into release/2.1 1e086d09e - updated branch aliases 2c7d9f62c - updated branch aliases 1899b4cc9 Merge commit '7139917230723d97b9b8c99e27dcd5c3f7d2e8e1' into release/2.1 00235777d - updated version number in upgrade notes 8305828a8 - prepared composer.json files for release 93b344eb8 - prepared composer.json files for release 66943d23c - prepared composer.json files for release 45b8ab62c - Updated dev.json and lock files to include `self.version` as internal pointer to packages of marello, marello-ee and subscriptions 6f02ef191 - Added twig file extension to deal with image id being fetched as a result in the product grid and getting the correct image during PostgreSQL having issue with an object in the result - Fixed subscription controller test to include cancelBeforeDuration field since it is not nullable 946e9f5ad MAR10001-13; Add template for invoice PDFs 52e93f598 MAR10001-13; Add InvoiceTableProvider b62251e0a MAR10001-13; Add PDF rendering helper libs a7d0a5c5a MAR10001-13; Add DocumentTableProviderPass 1a85d5761 MAR10001-13; Add get_document_tables() Twig function 71e130d39 MAR100001-13; Add DocumentTableProvider 3533f0c3e - added back sorters in to two datagrids for invoices and packingslips 3822f42fa - updated translations - updated updated orderitem status Dropshipped to Dropshipping - updated datagrids with removal of exports and some sorters 962e68cf9 - updated dev.lock files 7fa927ad5 - updated dev json and lock file to include oro 3.1.9 - small changes regarding docker environment files dc8f6fffd - removed todo comment 7083776e1 - Removed inventory check when subscription product is in the order and the OrderItem status is being updated 12852a412 MAR10001-13; Add PDF download controller b19ea2df2 MAR10001-13; Add RenderParameterProviders 4e52e2c48 MAR10001-13; Add RenderParameterProviderPass ed5bb6862 MAR10001-13; Add RenderParametersProvider d1e393651 MAR10001-13; Add LogoProvider 0faa418fb MAR10001-13; Make Sales Channel config parent checkbox label configurable ba079996e MAR10001-13; Update controller and template 5b0069d8e MAR10001-13; Rename configuration scope 461b39b8e MAR10001-13; Restructure PDF configuration f9dfa7cc4 MAR10001-13; Add logo to PDF settings 12eb8ebcd MAR10001-13; Fix "default" label for Sales Channel configuration 2bf51133d MAR10001-13; Add configuration for values on PDF e9fa7de18 MAR10001-13; Add button for Sales Channel configuration 8dd84704d MBM10001-13; Add Sales Channel configuration controller and view 10038ee55 MAR10001-13; Add Sales Channel configuration 27b3ecf98 MAR10001-13; Add MPDF composer dependency dfd138dd4 MAR10001-13; Add PDF/HTML rendering services 531be1f60 - removed external warehouse inventory from AvailableInventoryProvider in order to not confuse users, external warehouse inventory should be taken into account in the inventory balancer 1761fb12a - changed show form when empty boolean on inventorylevel collection in order to prevent confusion for users 00e1c0a8a - updated code for checking bc breaks 676e1b3de - Fixed some classes in order to keep BC for upcoming release - Added some comment in which we couldn't keep BC (OrmFilterExtension) ec551535f - fix constructor argument in order to keep BC for 2.1 release e872a1a9e - fix constructor argument in order to keep BC for 2.1 release f2a950a40 - CS Fixes - updated the check for updating existing email templates migration 13fa3e6ef Merge commit '5bb4981c46c7990c597fb4344a600bcb969981c5' into feature/MAR10001-676 b9df51fe7 Merge commit '5bb4981c46c7990c597fb4344a600bcb969981c5' into feature/MAR10001-676 ec550b110 Merge commit '5bb4981c46c7990c597fb4344a600bcb969981c5' into feature/MAR10001-676 2c570eb88 - Updated translation for Order Item status report a84ee1f8b - Update InventoryItem view and update page for more consistency between the pages b951d0979 - Added check for Back/Pre-order quantities where the MaxInventoryQty is null - Updated test to check the new order count in DB after if the maxQty is set for back/pre-order a6bc21b60 - Updated tests for checking whether more than 1 product type has been registered in the application 7933c7f1f - Added tooltips for back/pre-order form fields - removed the 'relations' between purchase order and the backorder datetime field - updated translation labels for consistency - removed configuration for enabling / disabling usage of PO and datetime of backorder - added feature to set the pre-order allowed field to false and pre order datetime to null when PO has received items - added feature to set backorder datetime to null when PO has received items 6739a9f0a Merge branch 'develop' into feature/MAR10001-485-subscriptions 4d8910aa7 - CS fixes - Updated dev.lock 8b8a597d3 - CS fix 24b6230a3 Merge branch 'develop' into feature/MAR10001-485-subscriptions 971fff3fd Merge commit '5f12bd20893426c155ce26461eb08b0dc2da5297' into feature/MAR10001-670 54742dcf5 - Changed name of the placeholder data twig function for clarification - Added additional tests for checking the functions of the Additional data Placeholder Provider feature 81f2b5979 - removed references of subscription bundle in Marello CE core - added the ability add additional placeholder data to render blocks - added additional placeholder data to orders, purcahse orders, suppliers, products and salesChannels 41f5a488a - Updated send processor test to include coverage for sending correctly rendered emails 9f5f02bc6 - Added additional test for testing when the email template cannot be found - Changed the type, content and subject to be send to the emailNotificationSender in order to prevent sending notifications without proper rendering fb74794b2 - CS fixes - Fixed CreateProduct test in ProductController test to take the attribute family into account when creating new products 223955bc2 - CS fixes - Fixed CreateProduct test in ProductController test to take the attribute family into account when creating new products b03c42c4d Merge commit 'b1ef79ca49e261313c5007056254a522cc673dee' into feature/MAR10001-485-subscriptions 9618740d0 Merge commit 'e5280b59fd2f4e57a38bc08bb62f43344bae4ef0' into feature/MAR10001-485-subscriptions 4d2543e97 Merge branch 'bugfix/MAR10001-0-fix-email-template-tags' into feature/MAR10001-485-subscriptions ffed40a1a - added fixes for non escaped twig tags in replenishment and return emails 3611327c9 - update migration namespace since it's not an oro migration but a Marello one; just clarification no bugfix a1e49fe17 - fix email tags for Purhcase Order email templates f8902f0b1 - Added Migration to Order & Purchase Order bundles to update email template tags to make sure the custom tags are not being removed 30953e672 - fixed subscription view translation keys - Changed ownership of product attributes to Custom to allow attribute sets be created without the subscription attributes in it (system attributes are required to be added to all attribute families) d65986df4 - added documentation for registering product types - fixed some phpDocs in Subscription entity to include correct return types 5db86390b Merge branch 'feature/MAR10001-485-subscriptions' of https://github.com/marellocommerce/development-mono-repository into feature/MAR10001-485-subscriptions 0ffa5816a Merge branch 'develop' into feature/MAR10001-485-subscriptions 78ac796bb - Merge commit '22a02abd2e26d568767c7824dba7970017871107' into feature/MAR10001-499 5175feb86 - Merge commit '22a02abd2e26d568767c7824dba7970017871107' into feature/MAR10001-499 06a08de29 - Fix installing demo data with default product type b3090a1af MBM-10001-13; Create MarelloPdfBundle c3751cc1d Merge branch 'feature/MAR10001-0-fix-is-managed-inventory-level' into feature/MAR10001-485-subscriptions 21e729b2e - fixed issue with managedInventory interfering with updating inventoryLevels and or going from managed to unmanaged 8824f1792 - revert mark as skipped functional test of ProductController b8745d27f - CS fixes - Updated test-extended script in dev.json to test PSR in correct directory - Mark ProductCreate test as skipped because of failure with subscription extension enabled 3fe64e20d Merge commit 'd616475ea6c833b33481537e80d73a114b76df94' into feature/MAR10001-485-subscriptions 713991723 - fixing issue of calling undefined method in the EntitySerializer ce6c5c855 Merge branch 'brainOut-patch-1' into dist-master c464be3c3 - updated phpDoc return value of WFAStrategyInterface to enforce contract f7c8208d8 make Interface contract respected 62ddd5584 - remove setting datetime for pre-orders when purchase order is created 5c7ba688e - PSR2 fixes - Remove unused constant from config - Unified translations for Backorders and Pre-orders a88f979df Merge commit 'b55e936f3c6428ae97c2f440cd8df8e00e3a9127' into feature/MAR10001-377 c19aecaef Merge branch 'feature/MAR10001-604' into develop 39c55c1cb - Fix PSR2 cs fixes 6b125367a Merge commit '4b5ec7e8d02e9b7e079e43d024f18dc3a92a9c7d' into development a4792bafb Merge commit '40a4d43559a871bd237a9a91e61f6bb33f6db69e' into feature/MAR10001-604 f02add10d - added composer.lock to .gitignore - remove composer.lock files 25135fc67 - added composer.lock - added dev.lock - updated branch alias in marello package composer.json 372aaeb4a - added composer.lock - added dev.lock - updated branch alias in marello package composer.json 9d0cbab26 Add 'applications/marello-application-ee/' from commit 'd13f2b41e079dff4eee99f8874928459658daaff' 815e5ee1f Merge commit '11ab45ce3c32e2982aa62cb56e7336732d8baa04' as 'package/marello-enterprise' 91291d7b8 Merge commit '07371b34fa35e74b969b9d7f160d601853a300b6' as 'applications/marello-application' d13f2b41e - added marello as source - added formatting-code and language to install e53127b9f - Update application structure for 3.1.x OroPlatform cf3c44f10 - updated dependencies for Marello git-subtree-dir: package/marello git-subtree-split: 1999cc10e65e8f726b9b8d4ea5a2ba57c2fe7061 --- README.md | 3 +- composer.json | 12 +- phpcs.xml | 10 + .../AddressBundle/Entity/MarelloAddress.php | 8 +- .../Repository/MarelloAddressRepository.php | 74 ++ .../Schema/v1_1/MarelloAddressBundle.php | 1 + .../Resources/config/jsmodules.yml | 4 + .../Resources/config/oro/placeholders.yml | 10 + .../Resources/config/requirejs.yml | 3 - .../Resources/config/services.yml | 11 +- .../Resources/public/js/address.js | 21 +- .../js/app/components/address-component.js | 48 ++ .../views/Address/addressMap.html.twig} | 2 +- .../Api/DataFixtures/LoadAddressData.php | 4 - .../Api/MarelloAddressJsonApiTest.php | 2 +- .../AddressBundle/Twig/AddressExtension.php | 6 +- .../MarelloBankTransferExtension.php | 33 + .../Entity/BankTransferSettings.php | 98 +++ .../BankTransferSettingsRepository.php | 38 ++ .../Form/Type/BankTransferOptionsType.php | 48 ++ .../Form/Type/BankTransferSettingsType.php | 63 ++ .../Integration/BankTransferChannelType.php | 27 + .../Integration/BankTransferTransport.php | 42 ++ .../MarelloBankTransferBundle.php | 21 + .../Method/BankTransferMethod.php | 102 +++ .../BankTransferMethodFromChannelFactory.php | 82 +++ .../Provider/BankTransferMethodProvider.php | 21 + .../MarelloBankTransferBundleInstaller.php | 66 ++ .../Schema/v1_0/MarelloBankTransferBundle.php | 59 ++ .../Resources/config/factory.yml | 8 + .../Resources/config/integration.yml | 16 + .../Resources/config/oro/actions.yml | 153 +++++ .../Resources/config/oro/bundles.yml | 2 + .../Resources/config/payment.yml | 11 + .../Resources/config/services.yml | 31 + .../public/img/bank-transfer-icon.png | Bin 0 -> 5253 bytes .../Resources/translations/messages.en.yml | 11 + .../bankTransferMethodWithOptions.html.twig | 5 + .../Controller/CategoryController.php | 23 +- .../Datagrid/CategoriesDatagridListener.php | 162 +++++ .../Resources/config/oro/datagrids.yml | 12 +- .../Resources/config/services.yml | 13 +- .../Resources/views/Category/index.html.twig | 2 +- .../Resources/views/Category/update.html.twig | 7 +- .../Resources/views/Category/view.html.twig | 1 + .../Datagrid/Property/categories.html.twig | 0 .../DataFixtures/LoadCategoryData.php | 8 - .../Bundle/CoreBundle/Mailer/Processor.php | 13 +- .../Bundle/CoreBundle/MarelloCoreBundle.php | 2 +- .../Migration/UpdateExtendRelationTrait.php | 76 +++ .../CoreBundle/Resources/config/jsmodules.yml | 3 + .../CoreBundle/Resources/config/oro/app.yml | 17 +- .../CoreBundle/Resources/config/requirejs.yml | 3 - .../CoreBundle/Resources/config/services.yml | 8 +- .../public/js/app/elements-helper.js | 4 +- .../sidebar_widgets/sticky_note/widget.yml | 11 + .../Resources/views/call_on_me.html.twig | 2 +- .../Serializer/EntitySerializer.php | 52 +- .../AdditionalPlaceholderProviderTest.php | 7 +- .../Tests/Unit/WorkflowTransitActionTest.php | 17 +- .../Bundle/CoreBundle/Twig/CoreExtension.php | 6 +- .../Bundle/CoreBundle/Twig/FileExtension.php | 6 +- .../CoreBundle/Twig/WorkflowExtension.php | 6 +- .../Validator/GreaterThanDateValidator.php | 15 +- .../Autocomplete/CompanyCustomerHandler.php | 2 +- .../Controller/CompanyController.php | 28 +- .../Controller/CustomerController.php | 121 ++++ .../Bundle/CustomerBundle/Entity/Company.php | 3 +- .../Bundle/CustomerBundle/Entity/Customer.php | 315 +++++++++ .../Entity/HasEmailAddressTrait.php | 2 +- .../Entity/HasFullNameTrait.php | 2 +- .../CompanyCustomersSelectGridListener.php | 2 +- .../Form/Handler/CompanyHandler.php | 2 +- .../Form/Handler/CustomerHandler.php | 4 +- .../Type/CompanyAwareCustomerSelectType.php | 3 +- .../CustomerBundle/Form/Type/CompanyType.php | 2 +- .../Form/Type/CustomerSelectType.php | 10 +- .../Form/Type/CustomerType.php | 5 +- .../Schema/MarelloCustomerBundleInstaller.php | 135 +++- .../Schema/v1_2/MigrateExtendedData.php | 51 ++ .../Schema/v1_2/MigrateForeignKeys.php | 89 +++ .../Migrations/Schema/v1_2/MigrateTable.php | 168 +++++ .../UpdateEntityConfigExtendClassQuery.php | 163 +++++ .../CustomerBundle/Model/ExtendCustomer.php | 17 + .../Provider/CustomerAddressProvider.php} | 16 +- .../Provider/CustomerEmailOwnerProvider.php | 5 +- .../Resources/config/jsmodules.yml | 10 + .../Resources/config/oro/acls.yml | 30 + .../Resources/config/oro/datagrids.yml | 125 +++- .../Resources/config/oro/navigation.yml | 19 + .../Resources/config/oro/routing.yml | 5 + .../Resources/config/oro/search.yml | 31 + .../Resources/config/requirejs.yml | 3 - .../Resources/config/services.yml | 73 +- ...-customer-select-create-component-mixin.js | 4 +- ...s => customer-company-select-component.js} | 11 +- .../customer-selection-component.js | 9 +- ...ny-customer-inline-type-async-component.js | 14 + ...-company-customer-inline-type-component.js | 14 + ...autocomplete-company-customer-component.js | 4 +- .../Resources/translations/messages.en.yml | 24 + .../Resources/views/Company/update.html.twig | 9 +- .../Resources/views/Company/view.html.twig | 3 +- .../Customer/Autocomplete/result.html.twig | 0 .../Customer/Autocomplete/selection.html.twig | 0 .../Resources/views/Customer/index.html.twig | 13 + .../views/Customer/searchResult.html.twig | 13 + .../Resources/views/Customer/update.html.twig | 28 +- .../Resources/views/Customer/view.html.twig | 74 +- .../Resources/views/Form/fields.html.twig | 47 +- .../Functional/Api/CustomerJsonApiTest.php | 6 +- .../Api/requests/customer_address_update.yml | 0 .../requests/customer_create_with_address.yml | 0 .../customer_create_without_address.yml | 0 .../Api/requests/customer_email_update.yml | 0 .../Api/responses/cget_customer_list.yml | 0 .../Api/responses/get_customer_by_email.yml | 0 .../Api/responses/get_customer_by_id.yml | 0 .../Controller/CompanyControllerTest.php | 2 +- .../Controller/CustomerControllerTest.php | 6 +- .../DataFixtures/LoadCompanyData.php | 3 +- .../DataFixtures/LoadCustomerData.php | 5 +- .../DataFixtures/dictionaries/customers.csv | 0 .../ParentCompanySearchHandlerTest.php | 9 +- .../Provider/CustomerAddressProviderTest.php} | 20 +- .../CustomerBundle/Twig/CustomerExtension.php | 6 +- .../Resources/config/jsmodules.yml | 16 + .../Resources/config/requirejs.yml | 6 - .../Resources/config/services.yml | 6 +- .../datagrid/cell/boolean-select-row-cell.js | 13 +- .../header-cell/select-all-header-cell.js | 4 +- .../datagrid/listener/column-form-listener.js | 4 +- .../datagrid/listener/select-all-listener.js | 4 +- .../Data/Demo/ORM/LoadCategoryData.php | 1 + .../Data/Demo/ORM/LoadCustomerData.php | 13 +- .../Data/Demo/ORM/LoadOrderData.php | 2 +- .../Data/Demo/ORM/LoadPaymentRule.php | 60 ++ .../Data/Demo/ORM/LoadPaymentRuleConfig.php | 105 +++ .../Data/Demo/ORM/LoadPaymentTermData.php | 12 +- .../Data/Demo/ORM/LoadPdfConfiguration.php | 89 +++ .../Data/Demo/ORM/LoadProductData.php | 8 +- .../Data/Demo/ORM/LoadProductImageData.php | 2 + .../Data/Demo/ORM/LoadPurchaseOrderData.php | 2 +- .../Data/Demo/ORM/LoadSupplierData.php | 18 - .../Data/Demo/ORM/data/pdf_settings.yml | 27 + .../ORM/images/goodwaves-invoice-logo.png | Bin 0 -> 15229 bytes .../Resources/config/requirejs.yml | 3 - .../public/js/filter/select-filter.js | 21 +- .../Command/InventoryBalanceCommand.php | 104 ++- .../BalancedInventoryLevelController.php | 28 +- .../Controller/InventoryItemController.php | 46 +- .../Controller/InventoryLevelController.php | 62 +- .../Controller/WarehouseController.php | 17 +- .../InventoryBundle/Entity/InventoryBatch.php | 403 +++++++++++ .../InventoryBundle/Entity/InventoryItem.php | 62 +- .../InventoryBundle/Entity/InventoryLevel.php | 72 +- .../Entity/InventoryLevelLogRecord.php | 93 ++- .../Entity/WarehouseChannelGroupLink.php | 4 +- .../InventoryBundle/Entity/WarehouseGroup.php | 4 +- ...ancedInventoryUpdateAfterEventListener.php | 12 +- .../InventoryBatchCreationEventListener.php | 68 ++ ...ventoryBatchInventoryRebalanceListener.php | 105 +++ .../Doctrine/SetsPropertyValue.php | 2 +- .../WarehouseGroupLinkRebalanceListener.php | 1 - .../ExternalWarehouseEventListener.php | 13 +- ...nventoryBatchFromInventoryLevelFactory.php | 24 + .../InventoryBatchSubscriber.php | 132 ++++ .../InventoryLevelSubscriber.php | 34 +- .../Type/InventoryBatchCollectionType.php | 71 ++ .../Form/Type/InventoryBatchType.php | 118 ++++ .../Form/Type/InventoryItemType.php | 36 +- .../Type/InventoryLevelCollectionType.php | 2 - .../Type/InventoryLevelManageBatchesType.php | 47 ++ .../Form/Type/InventoryLevelType.php | 45 +- .../InventoryLevelDataConverter.php | 61 -- .../InventoryLevelImportDataConverter.php | 40 ++ .../Reader/AbstractInventoryLevelReader.php | 85 +++ .../Reader/InventoryLevelReader.php | 36 + .../Reader/InventoryLevelTemplateReader.php | 33 + .../Strategy/InventoryLevelUpdateStrategy.php | 120 ++-- .../TemplateFixture/InventoryLevelFixture.php | 19 +- .../InventoryBundle/Logging/ChartBuilder.php | 16 +- .../Manager/BalancedInventoryManager.php | 6 +- .../Manager/InventoryItemManager.php | 9 +- .../Manager/InventoryManager.php | 87 ++- .../Data/ORM/ModifyReplenishmentData.php | 3 +- ...dateInventoryLevelLogWithWarehouseName.php | 44 ++ .../MarelloInventoryBundleInstaller.php | 65 +- ...arelloInventoryBundleAddIsSystemColumn.php | 61 ++ ...MarelloInventoryBundleDropSystemColumn.php | 51 ++ .../MarelloInventoryBundleInventoryBatch.php | 94 +++ .../v2_4/CreateAndUpdateInventoryLevelLog.php | 71 ++ .../UpdateInventoryLevelLogForeignKey.php | 48 ++ .../BalancedInventoryHandler.php | 11 +- .../Model/ExtendInventoryBatch.php | 21 + .../Model/ExtendInventoryLevel.php | 21 + .../InventoryBalancer/InventoryBalancer.php | 23 +- .../Model/InventoryUpdateContext.php | 75 +-- .../Model/InventoryUpdateContextFactory.php | 3 + .../Model/InventoryUpdateContextValidator.php | 10 - .../BalancingStrategyChoicesProvider.php | 2 +- .../Provider/OrderWarehousesProvider.php | 31 +- .../Resources/config/batch_jobs.yml | 27 + .../Resources/config/eventlistener.yml | 15 + .../InventoryBundle/Resources/config/form.yml | 27 +- .../Resources/config/importexport.yml | 41 +- .../Resources/config/jsmodules.yml | 3 + .../Resources/config/oro/actions.yml | 27 + .../Resources/config/oro/datagrids.yml | 25 +- .../Resources/config/oro/navigation.yml | 3 +- .../Resources/config/requirejs.yml | 3 - .../Resources/config/services.yml | 21 +- .../marelloinventory-chart-component.js | 13 +- .../Resources/translations/jsmessages.en.yml | 5 + .../Resources/translations/messages.en.yml | 57 +- .../Resources/translations/validators.en.yml | 6 +- .../Datagrid/InventoryLevel/subject.html.twig | 2 +- .../Resources/views/Form/fields.html.twig | 134 +++- .../Resources/views/Inventory/index.html.twig | 2 + .../views/Inventory/update.html.twig | 25 +- .../Resources/views/Inventory/view.html.twig | 23 +- .../views/Inventory/widget/datagrid.html.twig | 1 - .../InventoryLevel/manageBatches.html.twig | 119 ++++ .../views/Warehouse/updateDefault.html.twig | 1 + .../Strategy/BalancerStrategyInterface.php | 2 - .../Controller/InventoryControllerTest.php | 6 +- .../Functional/Entity/InventoryItemTest.php | 12 +- .../Tests/Unit/Entity/InventoryItemTest.php | 4 +- .../Tests/Unit/Entity/InventoryLevelTest.php | 2 +- .../Unit/Model/InventoryUpdateContextTest.php | 18 +- .../AvailableInventoryFormProviderTest.php | 17 +- .../Provider/OrderWarehousesProviderTest.php | 8 +- .../Twig/InventoryTotalExtension.php | 10 +- .../Twig/WarehouseExtension.php | 6 +- .../Constraints/InventoryOrderOnDemand.php | 2 +- .../InventoryOrderOnDemandValidator.php | 10 +- .../Controller/InvoiceController.php | 22 +- .../InvoiceBundle/Entity/AbstractInvoice.php | 8 +- .../Entity/AbstractInvoiceItem.php | 1 - .../Manager/CreditmemoManager.php | 1 - .../Mapper/RefundToCreditmemoMapper.php | 6 +- .../Data/ORM/SendInvoiceEmailTemplate.php | 13 + .../marello_send_invoice.html.twig | 619 +++++++++++++++++ .../Schema/MarelloInvoiceBundleInstaller.php | 16 +- .../Schema/v1_2/MarelloInvoiceBundle.php | 36 + .../Pdf/Logo/InvoiceLogoPathProvider.php | 116 ++++ .../InvoiceLogoRenderParameterProvider.php | 35 + .../Pdf/Request/InvoicePdfRequestHandler.php | 117 ++++ .../Pdf/Table/InvoiceTableProvider.php | 157 +++++ .../Resources/config/oro/app.yml | 7 + .../Resources/config/oro/datagrids.yml | 6 +- .../Resources/config/services.yml | 53 ++ .../Resources/translations/messages.en.yml | 11 +- .../Resources/translations/pdf.en.yml | 18 + .../Resources/views/Invoice/view.html.twig | 12 +- .../Resources/views/Pdf/invoice.html.twig | 343 ++++++++++ .../Api/responses/cget_creditmemo_list.yml | 4 - .../cget_creditmemo_list_by_order.yml | 2 - .../Api/responses/cget_invoice_list.yml | 8 - .../responses/cget_invoice_list_by_order.yml | 2 - .../Api/responses/get_creditmemo_by_id.yml | 2 - .../get_creditmemo_by_invoiceNumber.yml | 2 - .../Api/responses/get_invoice_by_id.yml | 2 - .../get_invoice_by_invoiceNumber.yml | 2 - .../InvoiceDownloadPdfControllerTest.php | 78 +++ .../Pdf/Logo/InvoiceLogoPathProviderTest.php | 155 +++++ ...InvoiceLogoRenderParameterProviderTest.php | 108 +++ .../resized_image/logoprovider_resized.txt | 1 + .../Pdf/Table/InvoiceTableProviderTest.php | 283 ++++++++ .../Resources/config/jsmodules.yml | 5 + .../Resources/config/requirejs.yml | 5 - .../Resources/config/services.yml | 1 + .../app/components/form-changes-component.js | 13 +- .../public/js/app/views/abstract-item-view.js | 5 +- .../js/app/views/abstract-items-view.js | 6 +- .../CompositeFormChangesProviderTest.php | 3 +- .../DependencyInjection/Configuration.php | 41 -- .../MarelloLocaleExtension.php | 5 - .../LocaleBundle/Form/Type/LocaleType.php | 41 -- .../Manager/EmailTemplateManager.php | 121 +--- .../Model/LocaleAwareInterface.php | 10 - .../Model/LocalizationAwareInterface.php | 19 + .../LocaleBundle/Model/LocalizationTrait.php | 29 +- .../ChainEntityLocalizationProvider.php | 6 +- .../DefaultEntityLocalizationProvider.php | 6 +- .../EntityLocalizationProviderInterface.php | 10 +- .../AbstractTranslatableRepository.php | 146 ---- .../EmailTemplateTranslatableRepository.php | 51 -- .../LocaleBundle/Resources/config/form.yml | 8 - .../Resources/config/services.yml | 13 +- .../Resources/translations/messages.en.yml | 7 - .../ORM/LoadManualShippingIntegration.php | 4 - .../Resources/config/services.yml | 1 + .../LoadManualShippingIntegration.php | 54 ++ .../Controller/NotificationController.php | 16 +- .../MarelloNotificationExtension.php | 9 +- .../Email/SendProcessor.php | 25 +- .../Entity/Notification.php | 48 ++ .../MarelloNotificationBundleInstaller.php | 38 +- .../Schema/v1_0/MarelloNotificationBundle.php | 67 ++ .../Schema/v1_1/MarelloNotificationBundle.php | 52 ++ .../Model/AttachmentInterface.php | 16 + .../Model/StringAttachment.php | 60 ++ .../NotificationActivityListProvider.php | 2 +- .../NotificationAttachmentProvider.php | 74 ++ .../Resources/config/services.yml | 11 +- .../Resources/translations/messages.en.yml | 1 + .../Functional/Email/SendProcessorTest.php | 12 +- .../Workflow/SendNotificationAction.php | 55 +- .../Controller/CustomerController.php | 102 --- .../Controller/OrderAjaxController.php | 27 +- .../Controller/OrderController.php | 63 +- .../Controller/OrderDashboardController.php | 31 +- .../BasicOrderPaymentLineItemConverter.php | 73 ++ ...OrderPaymentLineItemConverterInterface.php | 17 + .../MarelloOrderExtension.php | 3 +- .../Bundle/OrderBundle/Entity/Customer.php | 4 + .../Bundle/OrderBundle/Entity/Order.php | 44 +- .../Bundle/OrderBundle/Entity/OrderItem.php | 19 +- .../Entity/Repository/OrderRepository.php | 28 +- .../OrderPaymentContextBuildingEvent.php | 43 ++ .../OrderInventoryAllocationListener.php | 10 +- .../Doctrine/OrderItemStatusListener.php | 14 +- ...PurchaseOrderWorkflowCompletedListener.php | 2 +- .../Factory/OrderPaymentContextFactory.php | 156 +++++ .../Form/Type/AbstractOrderAddressType.php | 12 +- .../Form/Type/OrderBillingAddressType.php | 2 +- .../Form/Type/OrderShippingAddressType.php | 2 +- .../OrderBundle/Form/Type/OrderType.php | 25 + .../ORM/UpdateCustomerWithShippingAddress.php | 8 +- .../Schema/MarelloOrderBundleInstaller.php | 112 +--- .../Schema/v1_1/MarelloOrderBundle.php | 4 - .../Schema/v1_10/MarelloOrderBundle.php | 42 ++ .../Schema/v1_2/MarelloOrderBundle.php | 4 - .../Schema/v1_9/MarelloOrderBundle.php | 4 - .../OrderBundle/Model/ExtendCustomer.php | 2 + .../OrderDashboardStatisticProvider.php | 17 +- .../OrderItemFormChangesProvider.php | 2 +- .../Provider/OrderItemsSubtotalProvider.php | 2 +- .../Provider/OrderLocalizationProvider.php | 6 +- ...OrderStatisticsCurrencyNumberFormatter.php | 86 +++ ...OrderStatisticsCurrencyNumberProcessor.php | 78 +++ .../PossiblePaymentMethodsProvider.php | 68 ++ .../Resources/config/controllers.yml | 9 + .../OrderBundle/Resources/config/form.yml | 33 +- .../Resources/config/jsmodules.yml | 10 + .../OrderBundle/Resources/config/oro/acls.yml | 15 - .../Resources/config/oro/actions.yml | 89 +++ .../OrderBundle/Resources/config/oro/api.yml | 4 +- .../Resources/config/oro/dashboards.yml | 14 +- .../Resources/config/oro/datagrids.yml | 120 +--- .../Resources/config/oro/entity.yml | 2 +- .../Resources/config/oro/navigation.yml | 20 +- .../Resources/config/oro/placeholders.yml | 10 - .../Resources/config/oro/routing.yml | 5 - .../Resources/config/oro/search.yml | 31 - .../Resources/config/oro/workflows.yml | 112 +++- .../Resources/config/requirejs.yml | 13 - .../OrderBundle/Resources/config/services.yml | 102 +-- .../OrderBundle/Resources/config/shipping.yml | 3 +- .../Resources/config/validation.yml | 2 + .../OrderBundle/Resources/doc/api/customer.md | 2 +- .../OrderBundle/Resources/doc/api/order.md | 3 +- ...ny-customer-inline-type-async-component.js | 15 - ...-company-customer-inline-type-component.js | 15 - .../no-payment-methods-available.html | 1 + .../possible-payment-methods-template.html | 36 + .../selected-payment-method-template.html | 10 + .../public/js/app/views/order-address-view.js | 56 +- .../js/app/views/order-discount-view.js | 11 +- .../public/js/app/views/order-item-view.js | 15 +- .../public/js/app/views/order-items-view.js | 7 +- .../public/js/app/views/order-totals-view.js | 19 +- .../views/possible-payment-methods-view.js | 280 ++++++++ .../views/possible-shipping-methods-view.js | 27 +- .../public/js/app/views/shipping-cost-view.js | 9 +- .../Resources/translations/jsmessages.en.yml | 6 +- .../Resources/translations/messages.en.yml | 13 +- .../Resources/views/Customer/index.html.twig | 13 - .../views/Customer/searchResult.html.twig | 13 - .../topProductsByItemsSold.html.twig | 2 +- .../Dashboard/topProductsByRevenue.html.twig | 2 +- .../Resources/views/Form/fields.html.twig | 56 +- .../Resources/views/Order/create.html.twig | 18 +- .../Resources/views/Order/index.html.twig | 2 +- .../Resources/views/Order/macros.html.twig | 37 + .../views/Order/searchResult.html.twig | 2 +- .../Resources/views/Order/update.html.twig | 1 + .../Resources/views/Order/view.html.twig | 31 +- .../views/Order/widget/address.html.twig | 55 +- .../Order/widget/updateAddress.html.twig | 59 +- .../Tests/Functional/Api/OrderJsonApiTest.php | 2 +- .../order_create_with_existing_customer.yml | 4 +- .../order_create_with_new_customer.yml | 1 + .../Api/responses/cget_order_list.yml | 10 - .../Api/responses/get_order_by_id.yml | 1 - .../responses/get_order_by_orderNumber.yml | 1 - .../Controller/OrderControllerTest.php | 8 +- .../Controller/OrderOnDemandWorkflowTest.php | 8 +- .../Functional/DataFixtures/LoadOrderData.php | 3 +- .../OrderWorkflowStartListenerTest.php | 8 +- .../Tests/Unit/Entity/OrderTest.php | 3 +- .../Provider/DiscountSubtotalProviderTest.php | 2 +- ...ashboardOrderItemsByStatusProviderTest.php | 3 +- .../OrderItemFormChangesProviderTest.php | 5 +- .../OrderItemsSubtotalProviderTest.php | 2 +- .../OrderLocalizationProviderTest.php | 4 +- .../Tests/Unit/Stub/StatusEnumClassStub.php | 17 + .../Tests/Unit/Twig/OrderExtensionTest.php | 50 +- .../AvailableInventoryValidatorTest.php | 5 +- .../OrderBundle/Twig/OrderExtension.php | 34 +- .../Validator/AvailableInventoryValidator.php | 17 +- .../OrderBundle/Workflow/OrderShipAction.php | 47 +- .../Controller/PackingSlipController.php | 27 +- .../PackingBundle/Entity/PackingSlip.php | 33 +- .../PackingBundle/Entity/PackingSlipItem.php | 44 +- ...ingslipItemsBatchNumbersColumnListener.php | 30 + .../Mapper/OrderToPackingSlipMapper.php | 64 +- .../Schema/MarelloPackingBundleInstaller.php | 5 +- .../Schema/v1_3/MarelloPackingBundle.php | 56 ++ .../Resources/config/oro/datagrids.yml | 13 +- .../Resources/config/services.yml | 5 + .../Resources/translations/messages.en.yml | 1 + .../Datagrid/inventoryBatches.html.twig | 3 + .../Tests/Unit/Entity/PackingSlipTest.php | 2 +- .../Mapper/OrderToPackingSlipMapperTest.php | 14 +- ...dsConfigsRuleToggleStatusActionHandler.php | 45 ++ ...aymentMethodEnabledByIdentifierChecker.php | 31 + ...hodEnabledByIdentifierCheckerInterface.php | 13 + .../Checker/PaymentRuleEnabledChecker.php | 35 + .../PaymentRuleEnabledCheckerInterface.php | 15 + .../AbstractPaymentMethodHasPaymentRules.php | 83 +++ .../Condition/HasApplicablePaymentMethods.php | 103 +++ .../PaymentMethodHasEnabledPaymentRules.php | 35 + .../PaymentMethodHasPaymentRules.php | 35 + .../Basic/BasicPaymentContextBuilder.php | 258 +++++++ .../BasicPaymentContextBuilderFactory.php | 35 + .../PaymentContextBuilderFactoryInterface.php | 16 + .../PaymentContextBuilderInterface.php | 99 +++ ...sicPaymentContextToRulesValueConverter.php | 51 ++ ...tContextToRulesValueConverterInterface.php | 15 + ...iteSupportsEntityPaymentContextFactory.php | 67 ++ .../Exception/UnsupportedEntityException.php | 7 + ...tsEntityPaymentContextFactoryInterface.php | 27 + .../Basic/BasicPaymentLineItemBuilder.php | 124 ++++ .../BasicPaymentLineItemBuilderFactory.php | 20 + ...PaymentLineItemBuilderFactoryInterface.php | 20 + .../PaymentLineItemBuilderInterface.php | 43 ++ .../DoctrinePaymentLineItemCollection.php | 18 + ...ctrinePaymentLineItemCollectionFactory.php | 26 + ...mentLineItemCollectionFactoryInterface.php | 16 + .../PaymentLineItemCollectionInterface.php | 43 ++ .../PaymentBundle/Context/PaymentContext.php | 127 ++++ .../PaymentContextFactoryInterface.php | 13 + .../Context/PaymentContextInterface.php | 72 ++ .../PaymentBundle/Context/PaymentLineItem.php | 80 +++ .../Context/PaymentLineItemInterface.php | 46 ++ .../PaymentMethodsConfigsRuleController.php | 154 +++++ ...ositePaymentMethodProviderCompilerPass.php | 34 + .../Compiler/TwigSandboxConfigurationPass.php | 43 ++ .../DependencyInjection/Configuration.php | 40 ++ .../MarelloPaymentExtension.php | 36 + .../Entity/PaymentMethodAwareInterface.php | 17 + .../Entity/PaymentMethodConfig.php | 143 ++++ .../Entity/PaymentMethodsConfigsRule.php | 289 ++++++++ .../PaymentMethodsConfigsRuleDestination.php | 314 +++++++++ ...ethodsConfigsRuleDestinationPostalCode.php | 125 ++++ .../PaymentMethodConfigRepository.php | 49 ++ .../PaymentMethodsConfigsRuleRepository.php | 188 ++++++ .../ApplicablePaymentMethodViewEvent.php | 121 ++++ .../Event/PaymentMethodConfigDataEvent.php | 56 ++ .../PaymentRuleViewMethodTemplateListener.php | 39 ++ .../DecoratedProductLineItemFactory.php | 48 ++ .../DestinationPostalCodeTransformer.php | 52 ++ .../DestinationCollectionTypeSubscriber.php | 43 ++ .../MethodConfigCollectionSubscriber.php | 87 +++ .../MethodConfigSubscriber.php | 89 +++ .../PaymentMethodsConfigsRuleHandler.php | 62 ++ .../PaymentMethodConfigCollectionType.php | 73 ++ .../Form/Type/PaymentMethodConfigType.php | 87 +++ .../Form/Type/PaymentMethodSelectType.php | 62 ++ ...ymentMethodsConfigsRuleDestinationType.php | 89 +++ .../Type/PaymentMethodsConfigsRuleType.php | 95 +++ .../Formatter/PaymentMethodLabelFormatter.php | 33 + .../PaymentBundle/MarelloPaymentBundle.php | 27 + .../AbstractParameterBagPaymentConfig.php | 28 + .../Method/Config/PaymentConfigInterface.php | 16 + .../BasicMethodRemovalEventDispatcher.php | 29 + .../BasicMethodRenamingEventDispatcher.php | 29 + .../Method/Event/MethodRemovalEvent.php | 31 + .../MethodRemovalEventDispatcherInterface.php | 12 + .../Method/Event/MethodRenamingEvent.php | 46 ++ ...MethodRenamingEventDispatcherInterface.php | 14 + .../IntegrationRemovalListener.php | 51 ++ .../EventListener/MethodRemovalListener.php | 77 +++ .../EventListener/MethodRenamingListener.php | 43 ++ ...aymentMethodDisableIntegrationListener.php | 48 ++ ...tegrationPaymentMethodFactoryInterface.php | 15 + .../BasicPaymentMethodDisableHandler.php | 13 + .../PaymentMethodDisableHandlerInterface.php | 17 + ...esPaymentMethodDisableHandlerDecorator.php | 77 +++ .../PaymentMethodIconAwareInterface.php | 13 + .../Method/PaymentMethodInterface.php | 36 + .../Method/PaymentMethodViewCollection.php | 125 ++++ .../Method/PaymentMethodViewFactory.php | 24 + .../AbstractPaymentMethodProvider.php | 73 ++ .../CompositePaymentMethodProvider.php | 60 ++ .../ChannelPaymentMethodProvider.php | 122 ++++ .../Method/Provider/PaymentMethodProvider.php | 95 +++ .../PaymentMethodProviderInterface.php | 25 + .../Data/ORM/CreateDefaultPaymentRule.php | 64 ++ .../Schema/MarelloPaymentBundleInstaller.php | 203 ++++++ .../Schema/v1_0/MarelloPaymentBundle.php | 195 ++++++ .../Model/ExtendPaymentMethodConfig.php | 10 + .../Model/ExtendPaymentMethodsConfigsRule.php | 10 + ...ndPaymentMethodsConfigsRuleDestination.php | 10 + ...ethodsConfigsRuleDestinationPostalCode.php | 10 + .../Model/LineItemOptionModel.php | 138 ++++ .../Bundle/PaymentBundle/Model/Surcharge.php | 94 +++ .../BasicPaymentMethodChoicesProvider.php | 52 ++ .../BasicPaymentMethodsViewsProvider.php | 81 +++ ...dPaymentMethodChoicesProviderDecorator.php | 51 ++ ...icMethodsConfigsRulesByContextProvider.php | 55 ++ ...ConfigsRulesByContextProviderInterface.php | 15 + ...onMethodsConfigsRulesByContextProvider.php | 55 ++ .../PaymentMethodChoicesProviderInterface.php | 13 + .../PaymentMethodsViewsProviderInterface.php | 16 + .../Resources/config/form_types.yml | 48 ++ .../Resources/config/jsmodules.yml | 3 + .../Resources/config/mass_action.yml | 31 + .../Resources/config/oro/actions.yml | 58 ++ .../Resources/config/oro/assets.yml | 3 + .../Resources/config/oro/bundles.yml | 2 + .../Resources/config/oro/datagrids.yml | 108 +++ .../Resources/config/oro/navigation.yml | 32 + .../Resources/config/oro/routing.yml | 4 + .../Resources/config/oro/twig.yml | 2 + .../Resources/config/services.yml | 246 +++++++ .../Resources/config/validation.yml | 27 + .../Resources/public/css/scss/main.scss | 6 + .../public/css/scss/payment-methods-grid.scss | 352 ++++++++++ .../Resources/public/css/scss/style.scss | 50 ++ .../js/app/views/payment-rule-method-view.js | 263 ++++++++ .../Resources/translations/jsmessages.en.yml | 16 + .../Resources/translations/messages.en.yml | 154 +++++ .../Resources/translations/validators.en.yml | 6 + .../Resources/views/Form/fields.html.twig | 65 ++ .../Datagrid/configurations.html.twig | 2 + .../Datagrid/destinations.html.twig | 10 + .../PaymentMethodsConfigsRule/index.html.twig | 17 + .../macros.html.twig | 24 + .../paymentMethodWithOptions.html.twig | 6 + .../update.html.twig | 132 ++++ .../PaymentMethodsConfigsRule/view.html.twig | 69 ++ ...icMethodsConfigsRulesFiltrationService.php | 48 ++ ...ConfigsRulesFiltrationServiceInterface.php | 20 + ...aymentMethodsConfigsRuleControllerTest.php | 487 ++++++++++++++ ...oadPaymentMethodConfigsWithFakeMethods.php | 83 +++ .../LoadPaymentMethodsConfigsRules.php | 79 +++ ...dPaymentMethodsConfigsRulesWithConfigs.php | 206 ++++++ .../Functional/DataFixtures/LoadUserData.php | 113 ++++ ...yment_method_configs_with_fake_methods.yml | 15 + .../data/payment_methods_configs_rules.yml | 27 + ...ent_methods_configs_rules_with_configs.yml | 205 ++++++ .../PaymentMethodConfigRepositoryTest.php | 51 ++ ...aymentMethodsConfigsRuleRepositoryTest.php | 248 +++++++ .../Helper/PaymentTermIntegrationTrait.php | 34 + ...ntMethodEnabledByIdentifierCheckerTest.php | 85 +++ .../Checker/PaymentRuleEnabledCheckerTest.php | 63 ++ .../HasApplicablePaymentMethodsTest.php | 144 ++++ .../PaymentMethodHasPaymentRulesTest.php | 148 ++++ .../Tests/Unit/Condition/ToStringStub.php | 12 + .../Context/AbstractPaymentLineItemTest.php | 62 ++ .../Basic/BasicPaymentContextBuilderTest.php | 208 ++++++ .../BasicPaymentContextBuilderFactoryTest.php | 46 ++ .../Basic/BasicPaymentLineItemBuilderTest.php | 48 ++ ...BasicPaymentLineItemBuilderFactoryTest.php | 39 ++ ...nePaymentLineItemCollectionFactoryTest.php | 42 ++ .../DoctrinePaymentLineItemCollectionTest.php | 23 + .../Unit/Context/PaymentContextMockTrait.php | 19 + .../Tests/Unit/Context/PaymentContextTest.php | 102 +++ .../Unit/Context/PaymentLineItemTest.php | 35 + ...aymentContextToRuleValuesConverterTest.php | 107 +++ .../Unit/Entity/PaymentMethodConfigTest.php | 26 + ...dsConfigsRuleDestinationPostalCodeTest.php | 26 + ...ymentMethodsConfigsRuleDestinationTest.php | 217 ++++++ .../PaymentMethodsConfigsRuleMockTrait.php | 19 + .../Entity/PaymentMethodsConfigsRuleTest.php | 32 + .../CompositePaymentMethodProviderTest.php | 71 ++ .../BasicMethodRemovalEventDispatcherTest.php | 37 + ...BasicMethodRenamingEventDispatcherTest.php | 38 ++ .../Method/Event/MethodRemovalEventTest.php | 17 + .../Method/Event/MethodRenamingEventTest.php | 19 + .../IntegrationRemovalListenerTest.php | 96 +++ .../MethodRenamingListenerTest.php | 59 ++ ...ntMethodDisableIntegrationListenerTest.php | 111 +++ ...ymentMethodDisableHandlerDecoratorTest.php | 151 +++++ .../PaymentMethodViewCollectionTest.php | 212 ++++++ .../Method/PaymentMethodViewFactoryTest.php | 53 ++ .../ChannelPaymentMethodProviderTest.php | 136 ++++ .../BasicPaymentMethodChoicesProviderTest.php | 135 ++++ ...mentMethodChoicesProviderDecoratorTest.php | 178 +++++ ...thodsConfigsRulesByContextProviderTest.php | 114 ++++ ...thodsConfigsRulesByContextProviderTest.php | 114 ++++ .../Stub/PaymentMethodProviderStub.php | 48 ++ .../Unit/Provider/Stub/PaymentMethodStub.php | 150 +++++ ...thodsConfigsRulesFiltrationServiceTest.php | 96 +++ .../Twig/PaymentMethodExtension.php | 111 +++ .../Controller/PaymentTermController.php | 43 +- .../Entity/PaymentTermSettings.php | 98 +++ .../PaymentTermSettingsRepository.php | 38 ++ .../PaymentTermMethodViewListener.php | 46 ++ .../Form/Type/PaymentTermChoiceType.php | 1 - .../Form/Type/PaymentTermSettingsType.php | 61 ++ .../Integration/PaymentTermChannelType.php | 27 + .../Integration/PaymentTermTransport.php | 42 ++ .../PaymentTermMethodFromChannelFactory.php | 82 +++ .../Method/PaymentTermMethod.php | 100 +++ .../Provider/PaymentTermMethodProvider.php | 21 + .../Data/ORM/LoadPaymentTermIntegration.php | 117 ++++ .../MarelloPaymentTermBundleInstaller.php | 44 +- .../Schema/v1_1/MarelloPaymentTermBundle.php | 63 ++ .../Provider/PaymentTermProvider.php | 2 +- .../Resources/config/form.yml | 1 + .../Resources/config/oro/actions.yml | 153 +++++ .../Resources/config/oro/datagrids.yml | 2 +- .../Resources/config/services.yml | 59 ++ .../public/img/payment-term-logo.png | Bin 0 -> 4650 bytes .../Resources/translations/messages.en.yml | 6 + .../views/PaymentTerm/index.html.twig | 2 +- .../views/PaymentTerm/update.html.twig | 5 +- .../views/PaymentTerm/view.html.twig | 1 + .../SystemConfigurationControllerTest.php | 5 +- .../LoadPaymentTermIntegration.php | 54 ++ ...aymentTermDeletePermissionProviderTest.php | 2 + .../Controller/DownloadController.php | 18 + .../DocumentTableProviderPass.php | 25 + .../RenderParameterProviderPass.php | 25 + .../CompilerPass/RequestHandlersPass.php | 25 + .../DependencyInjection/Configuration.php | 80 +++ .../MarelloPdfExtension.php | 30 + .../Exception/PaperSizeNotSetException.php | 11 + .../PdfBundle/Factory/PdfWriterFactory.php | 31 + .../Configurator/DocumentConfigurator.php | 77 +++ .../Type/WorkflowTransitionSelectType.php | 59 ++ .../PdfBundle/Lib/View/EmptyDisplayLine.php | 26 + .../Bundle/PdfBundle/Lib/View/EmptyLine.php | 46 ++ .../Bundle/PdfBundle/Lib/View/Line.php | 101 +++ .../Bundle/PdfBundle/Lib/View/Table.php | 90 +++ .../Bundle/PdfBundle/MarelloPdfBundle.php | 21 + .../Provider/DocumentTableProvider.php | 24 + .../Provider/Render/ConfigValuesProvider.php | 43 ++ .../Provider/Render/EntityProvider.php | 18 + .../Provider/Render/LocalizationProvider.php | 118 ++++ .../RenderParameterProviderInterface.php | 9 + .../Provider/RenderParametersProvider.php | 26 + .../Provider/TableProviderInterface.php | 10 + .../PdfBundle/Provider/TableSizeProvider.php | 93 +++ .../PdfBundle/Renderer/HtmlRenderer.php | 39 ++ .../PdfBundle/Renderer/TwigRenderer.php | 38 ++ .../Request/CompositePdfRequestHandler.php | 43 ++ .../Request/PdfRequestHandlerInterface.php | 21 + .../Resources/config/oro/bundles.yml | 2 + .../Resources/config/oro/routing.yml | 4 + .../config/oro/system_configuration.yml | 185 +++++ .../PdfBundle/Resources/config/services.yml | 106 +++ .../Resources/translations/messages.en.yml | 49 ++ .../Resources/translations/pdf.en.yml | 3 + .../Resources/views/Download/util.html.twig | 15 + .../DocumentTableProviderPassTest.php | 122 ++++ .../RenderParameterProviderPassTest.php | 122 ++++ .../MarelloPdfExtensionTest.php | 40 ++ .../Unit/Factory/PdfWriterFactoryTest.php | 24 + .../Unit/Lib/View/EmptyDisplayLineTest.php | 40 ++ .../Tests/Unit/Lib/View/EmptyLineTest.php | 66 ++ .../Tests/Unit/Lib/View/LineTest.php | 191 ++++++ .../Tests/Unit/Lib/View/TableTest.php | 178 +++++ .../Provider/DocumentTableProviderTest.php | 62 ++ .../Render/ConfigValuesProviderTest.php | 75 +++ .../Provider/Render/EntityProviderTest.php | 29 + .../Provider/RenderParametersProviderTest.php | 74 ++ .../Unit/Provider/TableSizeProviderTest.php | 186 ++++++ .../Tests/Unit/Renderer/HtmlRendererTest.php | 105 +++ .../Tests/Unit/Renderer/data/multiple.txt | 1 + .../Tests/Unit/Renderer/data/single.txt | 1 + .../Extension/DocumentTableExtensionTest.php | 63 ++ .../SendEmailTemplateAttachmentActionTest.php | 137 ++++ .../Condition/IsSendEmailTransitionTest.php | 94 +++ .../Twig/Extension/DocumentTableExtension.php | 31 + .../SendEmailTemplateAttachmentAction.php | 375 +++++++++++ .../Condition/IsSendEmailTransition.php | 70 ++ .../Controller/PricingController.php | 15 +- .../ChannelPricesDatagridListener.php | 190 ++++++ .../Datagrid/PricesDatagridListener.php | 208 ++++++ .../Form/EventListener/PricingSubscriber.php | 4 +- .../Type/AssembledChannelPriceListType.php | 2 + .../Form/Type/ProductChannelPriceType.php | 5 +- .../Formatter/LabelVATAwareFormatter.php | 2 +- .../Model/PricingAwareInterface.php | 1 - .../Resources/config/jsmodules.yml | 4 + .../Resources/config/oro/twig.yml | 2 + .../Resources/config/services.yml | 15 + .../js/app/views/channel-pricing-item-view.js | 4 +- .../app/views/channel-pricing-items-view.js | 4 +- .../Resources/translations/messages.en.yml | 1 - .../Property/defaultChannelPrices.html.twig | 7 + .../Datagrid/Property/defaultPrices.html.twig | 7 + .../Datagrid/Property/msrpPrices.html.twig | 7 + .../Property/specialChannelPrices.html.twig | 7 + .../Datagrid/Property/specialPrices.html.twig | 7 + .../Resources/views/Form/fields.html.twig | 199 +----- .../Provider/AbstractSubtotalProvider.php | 2 +- .../Api/requests/product_without_prices.yml | 18 +- .../Controller/PricingControllerTest.php | 9 +- .../Formatter/LabelVATAwareFormatterTest.php | 2 +- .../Provider/ChannelPriceProviderTest.php | 25 +- .../CompositeSubtotalProviderTest.php | 2 +- .../PricingBundle/Twig/PricingExtension.php | 8 +- .../ProductsAssignSalesChannelsProcessor.php | 80 +-- .../Controller/ProductController.php | 98 ++- .../Controller/VariantController.php | 30 +- .../Duplicator/ProductDuplicator.php | 4 +- .../Manager/ProductApiEntityManager.php | 2 +- .../Bundle/ProductBundle/Entity/Product.php | 339 ++++++++-- .../Entity/ProductChannelTaxRelation.php | 9 +- .../AttributeFormViewListener.php | 298 +++++++++ .../Datagrid/ProductGridListener.php | 60 ++ .../ProductAttributeFamilyEventListener.php | 44 -- .../Doctrine/ProductDropshipEventListener.php | 11 + .../AttributeFamilySubscriber.php | 90 --- .../Form/Handler/ProductHandler.php | 1 - .../ProductsSalesChannelsAssignHandler.php | 171 +++-- ...roductChannelTaxRelationCollectionType.php | 2 +- .../Form/Type/ProductSupplierRelationType.php | 27 + .../ProductBundle/Form/Type/ProductType.php | 39 +- .../ProductBundle/MarelloProductBundle.php | 12 +- .../ORM/AssignAttributesToDefaultFamily.php | 117 ++++ .../ORM/LoadDefaultAttributeFamilyData.php | 25 +- .../Data/ORM/MakeProductAttributesTrait.php | 2 +- ...pdateExistingProductsWithLocalizedName.php | 46 ++ .../Schema/MarelloProductBundleInstaller.php | 51 +- .../Schema/v1_3/MarelloProductBundle.php | 3 - .../Schema/v1_8/RemovePriceAndCostColumns.php | 26 + .../v1_9/AddLocalizedNamesToProduct.php | 70 ++ ...teAttachmentFieldConfigForProductImage.php | 28 + .../ProductBundle/Model/ExtendProduct.php | 23 +- .../ProductBundle/Model/ProductType.php | 9 - .../Model/ProductTypeInterface.php | 5 - .../ProductBundle/Resources/config/form.yml | 52 +- .../Resources/config/jsmodules.yml | 11 + .../Resources/config/oro/actions.yml | 6 +- .../Resources/config/oro/api.yml | 2 - .../Resources/config/oro/datagrids.yml | 216 ++---- .../Resources/config/oro/placeholders.yml | 4 +- .../Resources/config/oro/search.yml | 14 +- .../Resources/config/requirejs.yml | 5 - .../Resources/config/services.yml | 56 +- .../Resources/config/validation.yml | 7 +- .../Resources/doc/api/product.md | 9 +- ...nel-aware-select-create-component-mixin.js | 6 +- ...lect-create-inline-type-async-component.js | 9 +- .../select-create-inline-type-component.js | 9 +- ...ocomplete-sales-channel-aware-component.js | 4 +- .../app/views/product-channel-taxcode-view.js | 4 +- .../views/product-channel-taxcodes-view.js | 4 +- .../Resources/translations/messages.en.yml | 5 +- .../Resources/translations/validators.en.yml | 2 + .../Resources/views/Form/fields.html.twig | 35 +- .../Product/Autocomplete/result.html.twig | 2 +- .../Product/Autocomplete/selection.html.twig | 2 +- .../Property/defaultChannelPrices.html.twig | 5 - .../Datagrid/Property/defaultPrices.html.twig | 5 - .../Datagrid/Property/msrpPrices.html.twig | 5 - .../Property/specialChannelPrices.html.twig | 5 - .../Datagrid/Property/specialPrices.html.twig | 5 - .../Product/assignSalesChannels.html.twig | 1 + .../views/Product/createStepOne.html.twig | 1 + .../views/Product/createStepTwo.html.twig | 117 ++-- .../Resources/views/Product/index.html.twig | 2 +- .../Resources/views/Product/update.html.twig | 115 ++-- .../Resources/views/Product/view.html.twig | 112 ++-- .../views/Product/widget/info.html.twig | 14 +- .../views/Variant/Form/fields.html.twig | 13 +- .../Resources/views/Variant/update.html.twig | 1 + .../Functional/Api/ProductJsonApiTest.php | 11 +- .../Api/requests/product_create.yml | 33 +- .../Api/requests/product_update.yml | 24 +- .../Api/responses/cget_product_list.yml | 48 +- .../Api/responses/get_product_by_id.yml | 8 +- .../Api/responses/get_product_by_sku.yml | 8 +- .../ProductAttributeControllerTest.php | 29 +- .../Controller/ProductControllerTest.php | 17 +- .../DataFixtures/LoadProductData.php | 62 +- .../Tests/Unit/Entity/ProductTest.php | 4 +- .../Twig/DynamicFieldsExtension.php | 75 +++ .../ProductBundle/Twig/ProductExtension.php | 25 +- .../ProductSupplierRelationsDropship.php | 27 + ...ductSupplierRelationsDropshipValidator.php | 118 ++++ .../Controller/PurchaseOrderController.php | 62 +- .../Cron/PurchaseOrderAdviceCommand.php | 2 +- .../Entity/PurchaseOrderItem.php | 4 +- .../Datagrid/PurchaseOrderGridListener.php | 4 +- .../PurchaseOrderItemSubscriber.php | 2 +- .../Handler/PurchaseOrderCreateHandler.php | 9 +- .../PurchaseOrderCreateStepOneHandler.php | 7 - .../Handler/PurchaseOrderUpdateHandler.php | 2 +- .../MarelloPurchaseOrderBundleInstaller.php | 2 +- .../v1_1/MarelloPurchaseOrderBundle.php | 4 +- .../v1_2_1/MarelloPurchaseOrderBundle.php | 1 + .../v1_2_2/MarelloPurchaseOrderBundle.php | 8 +- .../v1_2_3/MarelloPurchaseOrderBundle.php | 8 +- .../v1_3_2/MarelloPurchaseOrderBundle.php | 35 + .../v1_3_3/MarelloPurchaseOrderBundle.php | 49 ++ .../Resources/config/form.yml | 6 +- .../Resources/config/jsmodules.yml | 11 + .../Resources/config/oro/datagrids.yml | 22 +- .../Resources/config/requirejs.yml | 3 - .../Resources/config/services.yml | 5 +- .../purchaseorder-product-component.js | 40 ++ .../js/app/views/purchaseorder-item-view.js | 4 +- .../js/app/views/purchaseorder-items-view.js | 4 +- .../js/app/views/purchaseorder-totals-view.js | 13 +- ...-purchase-order-item-candidates-builder.js | 8 +- .../Resources/public/js/multiple-entity.js | 101 ++- .../public/js/multiple-entity/model.js | 16 +- .../templates/multiple-entity.html | 3 - .../public/js/multiple-entity/view.js | 42 +- .../Resources/views/Form/fields.html.twig | 99 +-- .../views/PurchaseOrder/create.html.twig | 1 + .../PurchaseOrder/createStepOne.html.twig | 1 + .../PurchaseOrder/createStepTwo.html.twig | 2 +- .../views/PurchaseOrder/update.html.twig | 1 + .../views/PurchaseOrder/view.html.twig | 2 +- .../widget/productsBySupplier.html.twig | 68 +- ...derOnOrderOnDemandCreationListenerTest.php | 2 +- .../Twig/PurchaseOrderExtension.php | 6 +- .../Action/ReceivePurchaseOrderAction.php | 1 - .../Controller/RefundController.php | 39 +- .../Bundle/RefundBundle/Entity/Refund.php | 10 +- .../Form/Type/OrderItemRefundType.php | 6 +- .../Schema/MarelloRefundBundleInstaller.php | 5 +- .../Schema/v1_1/MarelloRefundBundle.php | 4 +- .../Schema/v1_3/MarelloRefundBundle.php | 44 ++ .../Resources/config/oro/datagrids.yml | 6 +- .../Resources/config/services.yml | 4 +- .../Resources/translations/messages.en.yml | 7 +- .../Resources/views/Form/fields.html.twig | 22 +- .../Resources/views/Refund/create.html.twig | 3 +- .../Resources/views/Refund/update.html.twig | 2 +- .../Resources/views/Refund/view.html.twig | 2 +- .../DataFixtures/LoadRefundData.php | 6 +- .../RefundBundle/Twig/RefundExtension.php | 31 +- .../Controller/ReportController.php | 14 +- .../DependencyInjection/Configuration.php | 2 +- .../MarelloReportExtension.php | 2 +- .../Resources/config/oro/datagrids.yml | 18 +- .../Resources/config/services.yml | 2 +- .../Controller/ReturnController.php | 38 +- .../Repository/ReturnItemRepository.php | 4 - .../ReturnBundle/Entity/ReturnEntity.php | 9 +- .../ReturnItemTypeSubscriber.php | 2 - .../EventListener/ReturnTypeSubscriber.php | 3 +- .../Manager/BusinessRuleManager.php | 3 - .../Manager/Rules/MarelloProductWarranty.php | 2 + .../Manager/Rules/MarelloRorWarranty.php | 2 + .../Schema/MarelloReturnBundleInstaller.php | 3 +- .../Schema/v1_1/MarelloReturnBundle.php | 5 - .../Schema/v1_2/MarelloReturnBundle.php | 5 - .../Schema/v1_4/MarelloReturnBundle.php | 35 + .../ReturnBundle/Resources/config/oro/api.yml | 2 - .../Resources/config/oro/datagrids.yml | 4 +- .../Resources/config/services.yml | 4 +- .../Resources/config/shipping.yml | 2 +- .../Resources/translations/messages.en.yml | 1 - .../Resources/views/Form/fields.html.twig | 11 +- .../Resources/views/Return/create.html.twig | 1 + .../Resources/views/Return/update.html.twig | 3 +- .../Resources/views/Return/view.html.twig | 2 +- .../Api/responses/cget_return_list.yml | 9 - .../Api/responses/get_return_by_id.yml | 1 - .../ReturnBundle/Twig/ReturnExtension.php | 8 +- .../Workflow/InspectionAction.php | 15 +- .../MassAction/StatusMassActionHandler.php | 2 +- src/Marello/Bundle/RuleBundle/Entity/Rule.php | 2 +- .../Schema/MarelloRuleBundleInstaller.php | 4 +- .../MarelloRuleBundleAddIsSystemColumn.php | 44 ++ .../MarelloRuleBundleDropSystemColumn.php | 39 ++ .../RuleBundle/Resources/config/services.yml | 15 +- .../Constraints/ExpressionLanguageSyntax.php | 16 + .../ExpressionLanguageSyntaxValidator.php | 37 + .../Config/SalesChannelScopeManager.php | 40 ++ .../Controller/ConfigController.php | 84 +++ .../Controller/SalesChannelController.php | 43 +- .../SalesChannelGroupController.php | 28 +- .../SalesBundle/Entity/SalesChannel.php | 4 +- .../SalesBundle/Entity/SalesChannelGroup.php | 4 +- .../Datagrid/SalesChannelGridListener.php | 32 + .../SalesChannelsDatagridListener.php | 162 +++++ ...ChannelGroupInventoryRebalanceListener.php | 13 - .../DefaultSalesChannelSubscriber.php | 4 +- .../Form/Type/SalesChannelType.php | 2 - .../Data/ORM/LoadSalesChannelData.php | 1 - .../Schema/MarelloSalesBundleInstaller.php | 6 +- .../Schema/v1_2/MarelloSalesBundle.php | 35 + .../MarelloSalesBundleAddIsSystemColumn.php | 44 ++ .../MarelloSalesBundleDropSystemColumn.php | 39 ++ .../Model/ChannelAwareInterface.php | 17 - .../Model/SalesChannelAwareInterface.php | 26 +- .../Model/SalesChannelsAwareInterface.php | 31 + .../Placeholder/PlaceholderFilter.php | 13 + .../SalesChannelConfigurationFormProvider.php | 32 + .../SalesChannelLocalizationProvider.php | 6 +- .../SalesBundle/Resources/config/form.yml | 27 +- .../Resources/config/jsmodules.yml | 8 + .../Resources/config/oro/datagrids.yml | 19 +- .../Resources/config/oro/navigation.yml | 1 + .../Resources/config/oro/placeholders.yml | 10 + .../Resources/config/oro/routing.yml | 5 + .../Resources/config/requirejs.yml | 4 - .../SalesBundle/Resources/config/services.yml | 50 +- .../sales-channel-select-component.js | 11 +- ...e-system-group-sales-channels-component.js | 4 +- .../Resources/translations/messages.en.yml | 10 +- .../Resources/views/Config/button.html.twig | 9 + .../views/Config/salesChannel.html.twig | 24 + .../Resources/views/Form/fields.html.twig | 88 --- .../Datagrid/Property/channels.html.twig | 0 .../views/SalesChannel/update.html.twig | 5 +- .../views/SalesChannelGroup/index.html.twig | 2 +- .../views/SalesChannelGroup/update.html.twig | 6 +- .../Tests/Unit/Entity/SalesChannelTest.php | 3 +- .../SalesChannelGroupListenerTest.php | 15 +- .../Doctrine/SalesChannelListenerTest.php | 7 +- .../Handler/SalesChannelGroupHandlerTest.php | 7 +- .../SalesChannelLocalizationProviderTest.php | 4 +- .../Tests/Unit/Twig/SalesExtensionTest.php | 3 +- .../SalesBundle/Twig/SalesExtension.php | 6 +- .../Basic/BasicShippingContextBuilder.php | 2 +- .../ShippingContextBuilderInterface.php | 2 +- .../Context/ShippingContext.php | 2 +- .../Context/ShippingContextInterface.php | 2 +- .../ShippingMethodsConfigsRuleController.php | 33 +- .../Entity/HasShipmentTrait.php | 4 +- .../ShippingMethodsConfigsRuleRepository.php | 2 +- .../Type/ShippingMethodsConfigsRuleType.php | 16 +- .../Integration/ShippingAwareInterface.php | 9 +- ...edShippingMethodConfigurationInterface.php | 11 - .../ComposedShippingMethodConfiguration.php | 63 -- ...osedShippingMethodConfigurationBuilder.php | 145 ---- ...ppingMethodConfigurationBuilderFactory.php | 15 - ...odConfigurationBuilderFactoryInterface.php | 11 - ...ingMethodConfigurationBuilderInterface.php | 70 -- ...edShippingMethodConfigurationInterface.php | 16 - ...edShippingMethodConfigurationInterface.php | 11 - ...stShippingMethodConfigurationInterface.php | 11 - ...edShippingMethodConfigurationInterface.php | 23 - ...yRulesShippingMethodValidatorDecorator.php | 2 +- .../BasicShippingMethodChoicesProvider.php | 2 +- .../ShippingBundle/Resources/config/form.yml | 2 +- .../Resources/config/jsmodules.yml | 3 + .../Resources/config/mass_action.yml | 3 + .../Resources/config/oro/datagrids.yml | 8 +- .../Resources/config/requirejs.yml | 3 - .../Resources/config/services.yml | 24 +- .../Resources/config/validation.yml | 37 + .../js/app/views/shipping-rule-method-view.js | 17 +- .../Resources/translations/messages.en.yml | 11 - .../index.html.twig | 2 +- .../update.html.twig | 22 +- ...ippingMethodsConfigsRuleControllerTest.php | 516 ++++++++++++++ ...adShippingMethodConfigsWithFakeMethods.php | 83 +++ ...ShippingMethodTypeConfigsWithFakeTypes.php | 83 +++ .../LoadShippingMethodsConfigsRules.php | 79 +++ ...ShippingMethodsConfigsRulesWithConfigs.php | 223 +++++++ .../Functional/DataFixtures/LoadUserData.php | 113 ++++ ...pping_method_configs_with_fake_methods.yml | 15 + ...ng_method_type_configs_with_fake_types.yml | 11 + .../data/shipping_methods_configs_rules.yml | 27 + ...ing_methods_configs_rules_with_configs.yml | 205 ++++++ .../ShippingMethodConfigRepositoryTest.php | 90 +++ ...ShippingMethodTypeConfigRepositoryTest.php | 92 +++ ...ippingMethodsConfigsRuleRepositoryTest.php | 248 +++++++ .../Helper/ManualShippingIntegrationTrait.php | 42 ++ ...ngMethodEnabledByIdentifierCheckerTest.php | 85 +++ .../ShippingRuleEnabledCheckerTest.php | 63 ++ .../HasApplicableShippingMethodsTest.php | 144 ++++ .../ShippingMethodHasShippingRulesTest.php | 148 ++++ .../Tests/Unit/Condition/ToStringStub.php | 12 + .../Context/AbstractShippingLineItemTest.php | 62 ++ .../Basic/BasicShippingContextBuilderTest.php | 188 ++++++ ...BasicShippingContextBuilderFactoryTest.php | 53 ++ .../BasicShippingLineItemBuilderTest.php | 48 ++ ...icLineItemBuilderByLineItemFactoryTest.php | 49 ++ ...asicShippingLineItemBuilderFactoryTest.php | 39 ++ ...eShippingLineItemCollectionFactoryTest.php | 42 ++ ...DoctrineShippingLineItemCollectionTest.php | 23 + .../ShippingContextCacheKeyGeneratorTest.php | 406 +++++++++++ .../Unit/Context/ShippingContextMockTrait.php | 19 + .../Unit/Context/ShippingContextTest.php | 102 +++ .../Unit/Context/ShippingLineItemTest.php | 35 + ...ippingContextToRuleValuesConverterTest.php | 105 +++ .../Unit/Entity/ShippingMethodConfigTest.php | 28 + .../Entity/ShippingMethodTypeConfigTest.php | 27 + ...dsConfigsRuleDestinationPostalCodeTest.php | 26 + ...ppingMethodsConfigsRuleDestinationTest.php | 217 ++++++ .../ShippingMethodsConfigsRuleMockTrait.php | 19 + .../Entity/ShippingMethodsConfigsRuleTest.php | 32 + .../CompositeShippingMethodProviderTest.php | 67 ++ .../BasicMethodRemovalEventDispatcherTest.php | 37 + ...BasicMethodRenamingEventDispatcherTest.php | 38 ++ ...icMethodTypeRemovalEventDispatcherTest.php | 38 ++ .../Method/Event/MethodRemovalEventTest.php | 17 + .../Method/Event/MethodRenamingEventTest.php | 19 + .../Event/MethodTypeRemovalEventTest.php | 19 + .../IntegrationRemovalListenerTest.php | 96 +++ .../MethodRenamingListenerTest.php | 59 ++ ...ngMethodDisableIntegrationListenerTest.php | 111 +++ ...ppingMethodDisableHandlerDecoratorTest.php | 151 +++++ .../ChannelShippingMethodProviderTest.php | 136 ++++ .../BasicMethodTypeLabelsProviderTest.php | 110 +++ ...tableMethodTypeIdentifiersProviderTest.php | 87 +++ .../ShippingMethodViewCollectionTest.php | 599 +++++++++++++++++ .../Method/ShippingMethodViewFactoryTest.php | 225 +++++++ .../Stub/TrackingAwareShippingMethodStub.php | 81 +++ ...ackingAwareShippingMethodsProviderTest.php | 100 +++ .../BasicShippingMethodValidatorTest.php | 47 ++ ...esShippingMethodValidatorDecoratorTest.php | 307 +++++++++ ...idatorResultErrorCollectionBuilderTest.php | 41 ++ ...thodValidatorResultErrorCollectionTest.php | 18 + ...gMethodValidatorResultErrorFactoryTest.php | 22 + ...ShippingMethodValidatorResultErrorTest.php | 17 + ...ippingMethodValidatorResultFactoryTest.php | 43 ++ ...erBagShippingMethodValidatorResultTest.php | 28 + ...BasicShippingMethodChoicesProviderTest.php | 135 ++++ .../Provider/Cache/ShippingPriceCacheTest.php | 152 +++++ ...hodsShippingPriceProviderDecoratorTest.php | 158 +++++ ...pingMethodChoicesProviderDecoratorTest.php | 178 +++++ ...thodsConfigsRulesByContextProviderTest.php | 114 ++++ ...thodsConfigsRulesByContextProviderTest.php | 115 ++++ .../Stub/PriceAwareShippingMethodStub.php | 19 + .../Provider/Stub/ShippingAddressStub.php | 9 + .../Stub/ShippingMethodProviderStub.php | 54 ++ .../Unit/Provider/Stub/ShippingMethodStub.php | 181 +++++ ...hippingMethodTypeConfigTypeOptionsStub.php | 22 + .../Provider/Stub/ShippingMethodTypeStub.php | 120 ++++ .../test/ShippingPriceProviderTest.php | 630 ++++++++++++++++++ ...thodsConfigsRulesFiltrationServiceTest.php | 96 +++ .../Tools/FilteredDatagridRouteHelperTest.php | 67 ++ .../ShippingMethodLabelTranslator.php | 2 +- .../Twig/ShippingMethodExtension.php | 14 +- .../Controller/SupplierController.php | 78 ++- .../SupplierDropshipEventListener.php | 14 + .../ORM/UpdateCurrentSupplierWithCurrency.php | 6 +- .../Provider/SupplierProvider.php | 18 +- .../SupplierBundle/Resources/config/form.yml | 2 +- .../Resources/config/jsmodules.yml | 4 + .../Resources/config/oro/datagrids.yml | 14 +- .../Resources/config/oro/twig.yml | 2 + .../Resources/config/services.yml | 12 +- .../js/app/views/product-supplier-view.js | 14 +- .../js/app/views/product-suppliers-view.js | 6 +- .../Resources/views/Supplier/create.html.twig | 1 + .../Resources/views/Supplier/update.html.twig | 1 + .../Resources/views/Supplier/view.html.twig | 4 +- .../views/Supplier/widget/address.html.twig | 32 +- .../Supplier/widget/updateAddress.html.twig | 38 +- .../Controller/SupplierControllerTest.php | 4 +- .../SupplierBundle/Twig/SupplierExtension.php | 6 +- .../Controller/TaxCodeController.php | 41 +- .../Controller/TaxJurisdictionController.php | 37 +- .../Controller/TaxRateController.php | 41 +- .../Controller/TaxRuleController.php | 41 +- .../MarelloTaxExtension.php | 2 +- .../Bundle/TaxBundle/Entity/ZipCode.php | 1 - .../Datagrid/TaxCodeDatagridListener.php | 152 +++++ .../Schema/v1_3/MarelloTaxBundle.php | 1 - .../TaxCode/TaxCodesChoicesProvider.php | 1 - .../Provider/TaxSubtotalProvider.php | 2 +- .../TaxBundle/Resources/config/form.yml | 19 +- .../Resources/config/oro/datagrids.yml | 6 +- .../config/{service.yml => services.yml} | 25 +- .../Resources/views/Form/fields.html.twig | 11 +- .../Resources/views/TaxCode/create.html.twig | 5 +- .../Resources/views/TaxCode/update.html.twig | 5 +- .../Resources/views/TaxCode/view.html.twig | 2 +- .../views/TaxJurisdiction/index.html.twig | 2 +- .../views/TaxJurisdiction/update.html.twig | 6 +- .../views/TaxJurisdiction/view.html.twig | 1 + .../Resources/views/TaxRate/create.html.twig | 5 +- .../Resources/views/TaxRate/update.html.twig | 5 +- .../Resources/views/TaxRate/view.html.twig | 2 +- .../Resources/views/TaxRule/create.html.twig | 5 +- .../Resources/views/TaxRule/update.html.twig | 5 +- .../Resources/views/TaxRule/view.html.twig | 2 +- .../Operation/TaxCodeDeleteOperationTest.php | 4 +- .../TaxJurisdictionDeleteOperationTest.php | 8 +- .../Operation/TaxRateDeleteOperationTest.php | 4 +- .../Operation/TaxRuleDeleteOperationTest.php | 6 +- .../Calculator/ExcludedTaxCalculatorTest.php | 8 +- .../Unit/Provider/TaxSubtotalProviderTest.php | 2 +- .../Tests/Unit/Resolver/TotalResolverTest.php | 4 +- .../Controller/AjaxUPSController.php | 24 +- .../Data/ORM/Config/ChannelByTypeFactory.php | 2 +- .../UPSBundle/Resources/config/jsmodules.yml | 3 + .../UPSBundle/Resources/config/requirejs.yml | 3 - .../UPSBundle/Resources/config/services.yml | 1 + .../ups-transport-settings-component.js | 15 +- .../Unit/Factory/PriceRequestFactoryTest.php | 2 +- 1108 files changed, 41971 insertions(+), 5221 deletions(-) create mode 100644 phpcs.xml create mode 100644 src/Marello/Bundle/AddressBundle/Entity/Repository/MarelloAddressRepository.php create mode 100644 src/Marello/Bundle/AddressBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/AddressBundle/Resources/config/oro/placeholders.yml delete mode 100644 src/Marello/Bundle/AddressBundle/Resources/config/requirejs.yml create mode 100644 src/Marello/Bundle/AddressBundle/Resources/public/js/app/components/address-component.js rename src/Marello/Bundle/{OrderBundle/Resources/views/Customer/customerAddressMap.html.twig => AddressBundle/Resources/views/Address/addressMap.html.twig} (91%) create mode 100644 src/Marello/Bundle/BankTransferBundle/DependencyInjection/MarelloBankTransferExtension.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Entity/BankTransferSettings.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Entity/Repository/BankTransferSettingsRepository.php create mode 100755 src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferOptionsType.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferSettingsType.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Integration/BankTransferChannelType.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Integration/BankTransferTransport.php create mode 100644 src/Marello/Bundle/BankTransferBundle/MarelloBankTransferBundle.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Method/BankTransferMethod.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Method/Factory/BankTransferMethodFromChannelFactory.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Method/Provider/BankTransferMethodProvider.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Migrations/Schema/MarelloBankTransferBundleInstaller.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Migrations/Schema/v1_0/MarelloBankTransferBundle.php create mode 100644 src/Marello/Bundle/BankTransferBundle/Resources/config/factory.yml create mode 100644 src/Marello/Bundle/BankTransferBundle/Resources/config/integration.yml create mode 100755 src/Marello/Bundle/BankTransferBundle/Resources/config/oro/actions.yml create mode 100644 src/Marello/Bundle/BankTransferBundle/Resources/config/oro/bundles.yml create mode 100644 src/Marello/Bundle/BankTransferBundle/Resources/config/payment.yml create mode 100644 src/Marello/Bundle/BankTransferBundle/Resources/config/services.yml create mode 100644 src/Marello/Bundle/BankTransferBundle/Resources/public/img/bank-transfer-icon.png create mode 100644 src/Marello/Bundle/BankTransferBundle/Resources/translations/messages.en.yml create mode 100755 src/Marello/Bundle/BankTransferBundle/Resources/views/method/bankTransferMethodWithOptions.html.twig create mode 100644 src/Marello/Bundle/CatalogBundle/EventListener/Datagrid/CategoriesDatagridListener.php rename src/Marello/Bundle/{ProductBundle/Resources/views/Product => CatalogBundle/Resources/views}/Datagrid/Property/categories.html.twig (100%) create mode 100644 src/Marello/Bundle/CoreBundle/Migration/UpdateExtendRelationTrait.php create mode 100644 src/Marello/Bundle/CoreBundle/Resources/config/jsmodules.yml delete mode 100644 src/Marello/Bundle/CoreBundle/Resources/config/requirejs.yml create mode 100644 src/Marello/Bundle/CoreBundle/Resources/public/sidebar_widgets/sticky_note/widget.yml rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Autocomplete/CompanyCustomerHandler.php (97%) create mode 100644 src/Marello/Bundle/CustomerBundle/Controller/CustomerController.php create mode 100644 src/Marello/Bundle/CustomerBundle/Entity/Customer.php rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Entity/HasEmailAddressTrait.php (94%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Entity/HasFullNameTrait.php (98%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/EventListener/Datagrid/CompanyCustomersSelectGridListener.php (95%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Form/Handler/CustomerHandler.php (94%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Form/Type/CompanyAwareCustomerSelectType.php (92%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Form/Type/CustomerSelectType.php (69%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Form/Type/CustomerType.php (94%) create mode 100644 src/Marello/Bundle/CustomerBundle/Migrations/Schema/v1_2/MigrateExtendedData.php create mode 100644 src/Marello/Bundle/CustomerBundle/Migrations/Schema/v1_2/MigrateForeignKeys.php create mode 100644 src/Marello/Bundle/CustomerBundle/Migrations/Schema/v1_2/MigrateTable.php create mode 100644 src/Marello/Bundle/CustomerBundle/Migrations/Schema/v1_2/UpdateEntityConfigExtendClassQuery.php create mode 100644 src/Marello/Bundle/CustomerBundle/Model/ExtendCustomer.php rename src/Marello/Bundle/{OrderBundle/Provider/OrderCustomerAddressProvider.php => CustomerBundle/Provider/CustomerAddressProvider.php} (86%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Provider/CustomerEmailOwnerProvider.php (78%) create mode 100755 src/Marello/Bundle/CustomerBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/CustomerBundle/Resources/config/oro/acls.yml delete mode 100755 src/Marello/Bundle/CustomerBundle/Resources/config/requirejs.yml rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Resources/public/js/app/components/company-customer-select-create-component-mixin.js (95%) rename src/Marello/Bundle/CustomerBundle/Resources/public/js/app/components/{customer-compay-select-component.js => customer-company-select-component.js} (89%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Resources/public/js/app/components/customer-selection-component.js (87%) create mode 100644 src/Marello/Bundle/CustomerBundle/Resources/public/js/app/components/select-create-company-customer-inline-type-async-component.js create mode 100644 src/Marello/Bundle/CustomerBundle/Resources/public/js/app/components/select-create-company-customer-inline-type-component.js rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Resources/public/js/app/components/select2-autocomplete-company-customer-component.js (93%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Resources/views/Customer/Autocomplete/result.html.twig (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Resources/views/Customer/Autocomplete/selection.html.twig (100%) create mode 100644 src/Marello/Bundle/CustomerBundle/Resources/views/Customer/index.html.twig create mode 100644 src/Marello/Bundle/CustomerBundle/Resources/views/Customer/searchResult.html.twig rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Resources/views/Customer/update.html.twig (73%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Resources/views/Customer/view.html.twig (58%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/CustomerJsonApiTest.php (96%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/requests/customer_address_update.yml (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/requests/customer_create_with_address.yml (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/requests/customer_create_without_address.yml (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/requests/customer_email_update.yml (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/responses/cget_customer_list.yml (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/responses/get_customer_by_email.yml (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Api/responses/get_customer_by_id.yml (100%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/Controller/CustomerControllerTest.php (74%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/DataFixtures/LoadCustomerData.php (95%) rename src/Marello/Bundle/{OrderBundle => CustomerBundle}/Tests/Functional/DataFixtures/dictionaries/customers.csv (100%) rename src/Marello/Bundle/{OrderBundle/Tests/Unit/Provider/OrderCustomerAddressProviderTest.php => CustomerBundle/Tests/Unit/Provider/CustomerAddressProviderTest.php} (66%) create mode 100755 src/Marello/Bundle/DataGridBundle/Resources/config/jsmodules.yml delete mode 100755 src/Marello/Bundle/DataGridBundle/Resources/config/requirejs.yml create mode 100755 src/Marello/Bundle/DemoDataBundle/Migrations/Data/Demo/ORM/LoadPaymentRule.php create mode 100755 src/Marello/Bundle/DemoDataBundle/Migrations/Data/Demo/ORM/LoadPaymentRuleConfig.php create mode 100644 src/Marello/Bundle/DemoDataBundle/Migrations/Data/Demo/ORM/LoadPdfConfiguration.php create mode 100644 src/Marello/Bundle/DemoDataBundle/Migrations/Data/Demo/ORM/data/pdf_settings.yml create mode 100644 src/Marello/Bundle/DemoDataBundle/Migrations/Data/Demo/ORM/images/goodwaves-invoice-logo.png delete mode 100644 src/Marello/Bundle/FilterBundle/Resources/config/requirejs.yml create mode 100644 src/Marello/Bundle/InventoryBundle/Entity/InventoryBatch.php create mode 100644 src/Marello/Bundle/InventoryBundle/EventListener/Doctrine/InventoryBatchCreationEventListener.php create mode 100644 src/Marello/Bundle/InventoryBundle/EventListener/Doctrine/InventoryBatchInventoryRebalanceListener.php create mode 100644 src/Marello/Bundle/InventoryBundle/Factory/InventoryBatchFromInventoryLevelFactory.php create mode 100644 src/Marello/Bundle/InventoryBundle/Form/EventListener/InventoryBatchSubscriber.php create mode 100644 src/Marello/Bundle/InventoryBundle/Form/Type/InventoryBatchCollectionType.php create mode 100644 src/Marello/Bundle/InventoryBundle/Form/Type/InventoryBatchType.php create mode 100644 src/Marello/Bundle/InventoryBundle/Form/Type/InventoryLevelManageBatchesType.php delete mode 100644 src/Marello/Bundle/InventoryBundle/ImportExport/DataConverter/InventoryLevelDataConverter.php create mode 100644 src/Marello/Bundle/InventoryBundle/ImportExport/DataConverter/InventoryLevelImportDataConverter.php create mode 100644 src/Marello/Bundle/InventoryBundle/ImportExport/Reader/AbstractInventoryLevelReader.php create mode 100644 src/Marello/Bundle/InventoryBundle/ImportExport/Reader/InventoryLevelReader.php create mode 100644 src/Marello/Bundle/InventoryBundle/ImportExport/Reader/InventoryLevelTemplateReader.php create mode 100644 src/Marello/Bundle/InventoryBundle/Migrations/Data/ORM/UpdateInventoryLevelLogWithWarehouseName.php create mode 100644 src/Marello/Bundle/InventoryBundle/Migrations/Schema/v2_3/MarelloInventoryBundleAddIsSystemColumn.php create mode 100644 src/Marello/Bundle/InventoryBundle/Migrations/Schema/v2_3/MarelloInventoryBundleDropSystemColumn.php create mode 100644 src/Marello/Bundle/InventoryBundle/Migrations/Schema/v2_3/MarelloInventoryBundleInventoryBatch.php create mode 100644 src/Marello/Bundle/InventoryBundle/Migrations/Schema/v2_4/CreateAndUpdateInventoryLevelLog.php create mode 100644 src/Marello/Bundle/InventoryBundle/Migrations/Schema/v2_4/UpdateInventoryLevelLogForeignKey.php create mode 100644 src/Marello/Bundle/InventoryBundle/Model/ExtendInventoryBatch.php create mode 100644 src/Marello/Bundle/InventoryBundle/Model/ExtendInventoryLevel.php create mode 100644 src/Marello/Bundle/InventoryBundle/Resources/config/batch_jobs.yml create mode 100644 src/Marello/Bundle/InventoryBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/InventoryBundle/Resources/config/oro/actions.yml delete mode 100644 src/Marello/Bundle/InventoryBundle/Resources/config/requirejs.yml create mode 100644 src/Marello/Bundle/InventoryBundle/Resources/translations/jsmessages.en.yml create mode 100644 src/Marello/Bundle/InventoryBundle/Resources/views/InventoryLevel/manageBatches.html.twig create mode 100644 src/Marello/Bundle/InvoiceBundle/Migrations/Data/ORM/SendInvoiceEmailTemplate.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Migrations/Data/ORM/data/email_templates/marello_send_invoice.html.twig create mode 100644 src/Marello/Bundle/InvoiceBundle/Migrations/Schema/v1_2/MarelloInvoiceBundle.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Pdf/Logo/InvoiceLogoPathProvider.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Pdf/Logo/InvoiceLogoRenderParameterProvider.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Pdf/Request/InvoicePdfRequestHandler.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Pdf/Table/InvoiceTableProvider.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Resources/config/oro/app.yml create mode 100644 src/Marello/Bundle/InvoiceBundle/Resources/translations/pdf.en.yml create mode 100644 src/Marello/Bundle/InvoiceBundle/Resources/views/Pdf/invoice.html.twig create mode 100644 src/Marello/Bundle/InvoiceBundle/Tests/Functional/Controller/InvoiceDownloadPdfControllerTest.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Tests/Unit/Pdf/Logo/InvoiceLogoPathProviderTest.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Tests/Unit/Pdf/Logo/InvoiceLogoRenderParameterProviderTest.php create mode 100644 src/Marello/Bundle/InvoiceBundle/Tests/Unit/Pdf/Logo/data/public/resized_image/logoprovider_resized.txt create mode 100644 src/Marello/Bundle/InvoiceBundle/Tests/Unit/Pdf/Table/InvoiceTableProviderTest.php create mode 100644 src/Marello/Bundle/LayoutBundle/Resources/config/jsmodules.yml delete mode 100644 src/Marello/Bundle/LayoutBundle/Resources/config/requirejs.yml delete mode 100644 src/Marello/Bundle/LocaleBundle/DependencyInjection/Configuration.php delete mode 100644 src/Marello/Bundle/LocaleBundle/Form/Type/LocaleType.php delete mode 100644 src/Marello/Bundle/LocaleBundle/Model/LocaleAwareInterface.php create mode 100644 src/Marello/Bundle/LocaleBundle/Model/LocalizationAwareInterface.php delete mode 100644 src/Marello/Bundle/LocaleBundle/Repository/AbstractTranslatableRepository.php delete mode 100644 src/Marello/Bundle/LocaleBundle/Repository/EmailTemplateTranslatableRepository.php delete mode 100644 src/Marello/Bundle/LocaleBundle/Resources/config/form.yml delete mode 100644 src/Marello/Bundle/LocaleBundle/Resources/translations/messages.en.yml create mode 100644 src/Marello/Bundle/ManualShippingBundle/Tests/Functional/DataFixtures/LoadManualShippingIntegration.php create mode 100644 src/Marello/Bundle/NotificationBundle/Migrations/Schema/v1_0/MarelloNotificationBundle.php create mode 100644 src/Marello/Bundle/NotificationBundle/Migrations/Schema/v1_1/MarelloNotificationBundle.php create mode 100644 src/Marello/Bundle/NotificationBundle/Model/AttachmentInterface.php create mode 100644 src/Marello/Bundle/NotificationBundle/Model/StringAttachment.php create mode 100644 src/Marello/Bundle/NotificationBundle/Provider/NotificationAttachmentProvider.php delete mode 100644 src/Marello/Bundle/OrderBundle/Controller/CustomerController.php create mode 100644 src/Marello/Bundle/OrderBundle/Converter/BasicOrderPaymentLineItemConverter.php create mode 100644 src/Marello/Bundle/OrderBundle/Converter/OrderPaymentLineItemConverterInterface.php create mode 100644 src/Marello/Bundle/OrderBundle/Event/OrderPaymentContextBuildingEvent.php create mode 100644 src/Marello/Bundle/OrderBundle/Factory/OrderPaymentContextFactory.php create mode 100644 src/Marello/Bundle/OrderBundle/Migrations/Schema/v1_10/MarelloOrderBundle.php create mode 100644 src/Marello/Bundle/OrderBundle/Provider/OrderStatisticsCurrencyNumberFormatter.php create mode 100644 src/Marello/Bundle/OrderBundle/Provider/OrderStatisticsCurrencyNumberProcessor.php create mode 100644 src/Marello/Bundle/OrderBundle/Provider/PossiblePaymentMethodsProvider.php create mode 100644 src/Marello/Bundle/OrderBundle/Resources/config/controllers.yml create mode 100644 src/Marello/Bundle/OrderBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/OrderBundle/Resources/config/oro/actions.yml delete mode 100644 src/Marello/Bundle/OrderBundle/Resources/config/oro/placeholders.yml delete mode 100644 src/Marello/Bundle/OrderBundle/Resources/config/requirejs.yml delete mode 100644 src/Marello/Bundle/OrderBundle/Resources/public/js/app/components/select-create-company-customer-inline-type-async-component.js delete mode 100644 src/Marello/Bundle/OrderBundle/Resources/public/js/app/components/select-create-company-customer-inline-type-component.js create mode 100644 src/Marello/Bundle/OrderBundle/Resources/public/js/app/templates/no-payment-methods-available.html create mode 100644 src/Marello/Bundle/OrderBundle/Resources/public/js/app/templates/possible-payment-methods-template.html create mode 100644 src/Marello/Bundle/OrderBundle/Resources/public/js/app/templates/selected-payment-method-template.html create mode 100644 src/Marello/Bundle/OrderBundle/Resources/public/js/app/views/possible-payment-methods-view.js delete mode 100644 src/Marello/Bundle/OrderBundle/Resources/views/Customer/index.html.twig delete mode 100644 src/Marello/Bundle/OrderBundle/Resources/views/Customer/searchResult.html.twig create mode 100644 src/Marello/Bundle/OrderBundle/Tests/Unit/Stub/StatusEnumClassStub.php create mode 100644 src/Marello/Bundle/PackingBundle/EventListener/Datagrid/PackingslipItemsBatchNumbersColumnListener.php create mode 100644 src/Marello/Bundle/PackingBundle/Migrations/Schema/v1_3/MarelloPackingBundle.php create mode 100644 src/Marello/Bundle/PackingBundle/Resources/views/PackingSlip/Datagrid/inventoryBatches.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Action/Handler/PaymentMethodsConfigsRuleToggleStatusActionHandler.php create mode 100755 src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierChecker.php create mode 100755 src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierCheckerInterface.php create mode 100755 src/Marello/Bundle/PaymentBundle/Checker/PaymentRuleEnabledChecker.php create mode 100755 src/Marello/Bundle/PaymentBundle/Checker/PaymentRuleEnabledCheckerInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Condition/AbstractPaymentMethodHasPaymentRules.php create mode 100644 src/Marello/Bundle/PaymentBundle/Condition/HasApplicablePaymentMethods.php create mode 100644 src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasEnabledPaymentRules.php create mode 100644 src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasPaymentRules.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/BasicPaymentContextBuilder.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactory.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Builder/Factory/PaymentContextBuilderFactoryInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Builder/PaymentContextBuilderInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Converter/Basic/BasicPaymentContextToRulesValueConverter.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Converter/PaymentContextToRulesValueConverterInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Factory/CompositeSupportsEntityPaymentContextFactory.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Factory/Exception/UnsupportedEntityException.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/Factory/SupportsEntityPaymentContextFactoryInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Builder/Basic/BasicPaymentLineItemBuilder.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactory.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Builder/Factory/PaymentLineItemBuilderFactoryInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Builder/PaymentLineItemBuilderInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Collection/Doctrine/DoctrinePaymentLineItemCollection.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Collection/Doctrine/Factory/DoctrinePaymentLineItemCollectionFactory.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Collection/Factory/PaymentLineItemCollectionFactoryInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/LineItem/Collection/PaymentLineItemCollectionInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/PaymentContext.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/PaymentContextFactoryInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/PaymentContextInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/PaymentLineItem.php create mode 100644 src/Marello/Bundle/PaymentBundle/Context/PaymentLineItemInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Controller/PaymentMethodsConfigsRuleController.php create mode 100644 src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/CompositePaymentMethodProviderCompilerPass.php create mode 100644 src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/TwigSandboxConfigurationPass.php create mode 100644 src/Marello/Bundle/PaymentBundle/DependencyInjection/Configuration.php create mode 100644 src/Marello/Bundle/PaymentBundle/DependencyInjection/MarelloPaymentExtension.php create mode 100644 src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodAwareInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodConfig.php create mode 100644 src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRule.php create mode 100644 src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestination.php create mode 100644 src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestinationPostalCode.php create mode 100644 src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodConfigRepository.php create mode 100644 src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodsConfigsRuleRepository.php create mode 100644 src/Marello/Bundle/PaymentBundle/Event/ApplicablePaymentMethodViewEvent.php create mode 100755 src/Marello/Bundle/PaymentBundle/Event/PaymentMethodConfigDataEvent.php create mode 100644 src/Marello/Bundle/PaymentBundle/EventListener/PaymentRuleViewMethodTemplateListener.php create mode 100644 src/Marello/Bundle/PaymentBundle/ExpressionLanguage/DecoratedProductLineItemFactory.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/DataTransformer/DestinationPostalCodeTransformer.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/DestinationCollectionTypeSubscriber.php create mode 100755 src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigCollectionSubscriber.php create mode 100755 src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigSubscriber.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/Handler/PaymentMethodsConfigsRuleHandler.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigCollectionType.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigType.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodSelectType.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleDestinationType.php create mode 100644 src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleType.php create mode 100755 src/Marello/Bundle/PaymentBundle/Formatter/PaymentMethodLabelFormatter.php create mode 100644 src/Marello/Bundle/PaymentBundle/MarelloPaymentBundle.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Config/ParameterBag/AbstractParameterBagPaymentConfig.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Config/PaymentConfigInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Event/BasicMethodRemovalEventDispatcher.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Event/BasicMethodRenamingEventDispatcher.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEvent.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEventDispatcherInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Event/MethodRenamingEvent.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Event/MethodRenamingEventDispatcherInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/EventListener/IntegrationRemovalListener.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRemovalListener.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRenamingListener.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/EventListener/PaymentMethodDisableIntegrationListener.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Factory/IntegrationPaymentMethodFactoryInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Handler/BasicPaymentMethodDisableHandler.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Handler/PaymentMethodDisableHandlerInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Handler/RulesPaymentMethodDisableHandlerDecorator.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/PaymentMethodIconAwareInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/PaymentMethodInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/PaymentMethodViewCollection.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/PaymentMethodViewFactory.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Provider/AbstractPaymentMethodProvider.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Provider/CompositePaymentMethodProvider.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Provider/Integration/ChannelPaymentMethodProvider.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProvider.php create mode 100644 src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProviderInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Migrations/Data/ORM/CreateDefaultPaymentRule.php create mode 100644 src/Marello/Bundle/PaymentBundle/Migrations/Schema/MarelloPaymentBundleInstaller.php create mode 100644 src/Marello/Bundle/PaymentBundle/Migrations/Schema/v1_0/MarelloPaymentBundle.php create mode 100644 src/Marello/Bundle/PaymentBundle/Model/ExtendPaymentMethodConfig.php create mode 100644 src/Marello/Bundle/PaymentBundle/Model/ExtendPaymentMethodsConfigsRule.php create mode 100644 src/Marello/Bundle/PaymentBundle/Model/ExtendPaymentMethodsConfigsRuleDestination.php create mode 100644 src/Marello/Bundle/PaymentBundle/Model/ExtendPaymentMethodsConfigsRuleDestinationPostalCode.php create mode 100644 src/Marello/Bundle/PaymentBundle/Model/LineItemOptionModel.php create mode 100644 src/Marello/Bundle/PaymentBundle/Model/Surcharge.php create mode 100755 src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodChoicesProvider.php create mode 100644 src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodsViewsProvider.php create mode 100755 src/Marello/Bundle/PaymentBundle/Provider/EnabledPaymentMethodChoicesProviderDecorator.php create mode 100644 src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProvider.php create mode 100644 src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/MethodsConfigsRulesByContextProviderInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProvider.php create mode 100755 src/Marello/Bundle/PaymentBundle/Provider/PaymentMethodChoicesProviderInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Provider/PaymentMethodsViewsProviderInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/form_types.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/mass_action.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/oro/actions.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/oro/assets.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/oro/bundles.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/oro/datagrids.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/oro/navigation.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/oro/routing.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/oro/twig.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/services.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/config/validation.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/public/css/scss/main.scss create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/public/css/scss/payment-methods-grid.scss create mode 100755 src/Marello/Bundle/PaymentBundle/Resources/public/css/scss/style.scss create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/public/js/app/views/payment-rule-method-view.js create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/translations/jsmessages.en.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/translations/messages.en.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/translations/validators.en.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/Form/fields.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/configurations.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/destinations.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/index.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/macros.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/paymentMethodWithOptions.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/update.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/view.html.twig create mode 100644 src/Marello/Bundle/PaymentBundle/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationService.php create mode 100644 src/Marello/Bundle/PaymentBundle/RuleFiltration/MethodsConfigsRulesFiltrationServiceInterface.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/Controller/PaymentMethodsConfigsRuleControllerTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodConfigsWithFakeMethods.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRules.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRulesWithConfigs.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadUserData.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_method_configs_with_fake_methods.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules_with_configs.yml create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodConfigRepositoryTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodsConfigsRuleRepositoryTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Functional/Helper/PaymentTermIntegrationTrait.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentMethodEnabledByIdentifierCheckerTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentRuleEnabledCheckerTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/HasApplicablePaymentMethodsTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/PaymentMethodHasPaymentRulesTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/ToStringStub.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/AbstractPaymentLineItemTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/BasicPaymentContextBuilderTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactoryTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicPaymentLineItemBuilderTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactoryTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionFactoryTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextMockTrait.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentLineItemTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Converter/Basic/PaymentContextToRuleValuesConverterTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodConfigTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleDestinationPostalCodeTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleDestinationTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleMockTrait.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/CompositePaymentMethodProviderTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/PaymentMethodDisableIntegrationListenerTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Handler/RulesPaymentMethodDisableHandlerDecoratorTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewCollectionTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewFactoryTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Provider/ChannelPaymentMethodProviderTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/BasicPaymentMethodChoicesProviderTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/EnabledPaymentMethodChoicesProviderDecoratorTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodProviderStub.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodStub.php create mode 100644 src/Marello/Bundle/PaymentBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php create mode 100755 src/Marello/Bundle/PaymentBundle/Twig/PaymentMethodExtension.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Entity/PaymentTermSettings.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Entity/Repository/PaymentTermSettingsRepository.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/EventListener/PaymentTermMethodViewListener.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermSettingsType.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Integration/PaymentTermChannelType.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Integration/PaymentTermTransport.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Method/Factory/PaymentTermMethodFromChannelFactory.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Method/PaymentTermMethod.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Method/Provider/PaymentTermMethodProvider.php create mode 100755 src/Marello/Bundle/PaymentTermBundle/Migrations/Data/ORM/LoadPaymentTermIntegration.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/v1_1/MarelloPaymentTermBundle.php create mode 100644 src/Marello/Bundle/PaymentTermBundle/Resources/public/img/payment-term-logo.png create mode 100644 src/Marello/Bundle/PaymentTermBundle/Tests/Functional/DataFixtures/LoadPaymentTermIntegration.php create mode 100644 src/Marello/Bundle/PdfBundle/Controller/DownloadController.php create mode 100644 src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/DocumentTableProviderPass.php create mode 100644 src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RenderParameterProviderPass.php create mode 100644 src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RequestHandlersPass.php create mode 100644 src/Marello/Bundle/PdfBundle/DependencyInjection/Configuration.php create mode 100644 src/Marello/Bundle/PdfBundle/DependencyInjection/MarelloPdfExtension.php create mode 100644 src/Marello/Bundle/PdfBundle/Exception/PaperSizeNotSetException.php create mode 100644 src/Marello/Bundle/PdfBundle/Factory/PdfWriterFactory.php create mode 100644 src/Marello/Bundle/PdfBundle/Form/Configurator/DocumentConfigurator.php create mode 100644 src/Marello/Bundle/PdfBundle/Form/Type/WorkflowTransitionSelectType.php create mode 100644 src/Marello/Bundle/PdfBundle/Lib/View/EmptyDisplayLine.php create mode 100644 src/Marello/Bundle/PdfBundle/Lib/View/EmptyLine.php create mode 100644 src/Marello/Bundle/PdfBundle/Lib/View/Line.php create mode 100644 src/Marello/Bundle/PdfBundle/Lib/View/Table.php create mode 100644 src/Marello/Bundle/PdfBundle/MarelloPdfBundle.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/DocumentTableProvider.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/Render/ConfigValuesProvider.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/Render/EntityProvider.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/Render/LocalizationProvider.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/RenderParameterProviderInterface.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/RenderParametersProvider.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/TableProviderInterface.php create mode 100644 src/Marello/Bundle/PdfBundle/Provider/TableSizeProvider.php create mode 100644 src/Marello/Bundle/PdfBundle/Renderer/HtmlRenderer.php create mode 100644 src/Marello/Bundle/PdfBundle/Renderer/TwigRenderer.php create mode 100644 src/Marello/Bundle/PdfBundle/Request/CompositePdfRequestHandler.php create mode 100644 src/Marello/Bundle/PdfBundle/Request/PdfRequestHandlerInterface.php create mode 100644 src/Marello/Bundle/PdfBundle/Resources/config/oro/bundles.yml create mode 100644 src/Marello/Bundle/PdfBundle/Resources/config/oro/routing.yml create mode 100644 src/Marello/Bundle/PdfBundle/Resources/config/oro/system_configuration.yml create mode 100644 src/Marello/Bundle/PdfBundle/Resources/config/services.yml create mode 100644 src/Marello/Bundle/PdfBundle/Resources/translations/messages.en.yml create mode 100644 src/Marello/Bundle/PdfBundle/Resources/translations/pdf.en.yml create mode 100644 src/Marello/Bundle/PdfBundle/Resources/views/Download/util.html.twig create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/DocumentTableProviderPassTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/RenderParameterProviderPassTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/MarelloPdfExtensionTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Factory/PdfWriterFactoryTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyDisplayLineTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyLineTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/LineTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/TableTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/DocumentTableProviderTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/ConfigValuesProviderTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/EntityProviderTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/RenderParametersProviderTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/TableSizeProviderTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/HtmlRendererTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/multiple.txt create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/single.txt create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Twig/Extension/DocumentTableExtensionTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Action/SendEmailTemplateAttachmentActionTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Condition/IsSendEmailTransitionTest.php create mode 100644 src/Marello/Bundle/PdfBundle/Twig/Extension/DocumentTableExtension.php create mode 100644 src/Marello/Bundle/PdfBundle/Workflow/Action/SendEmailTemplateAttachmentAction.php create mode 100644 src/Marello/Bundle/PdfBundle/Workflow/Condition/IsSendEmailTransition.php create mode 100644 src/Marello/Bundle/PricingBundle/EventListener/Datagrid/ChannelPricesDatagridListener.php create mode 100644 src/Marello/Bundle/PricingBundle/EventListener/Datagrid/PricesDatagridListener.php create mode 100644 src/Marello/Bundle/PricingBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/PricingBundle/Resources/config/oro/twig.yml create mode 100644 src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultChannelPrices.html.twig create mode 100644 src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultPrices.html.twig create mode 100644 src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/msrpPrices.html.twig create mode 100644 src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialChannelPrices.html.twig create mode 100644 src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialPrices.html.twig create mode 100644 src/Marello/Bundle/ProductBundle/EventListener/AttributeFormViewListener.php delete mode 100644 src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductAttributeFamilyEventListener.php delete mode 100644 src/Marello/Bundle/ProductBundle/Form/EventListener/AttributeFamilySubscriber.php create mode 100644 src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/AssignAttributesToDefaultFamily.php create mode 100644 src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/UpdateExistingProductsWithLocalizedName.php create mode 100644 src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_8/RemovePriceAndCostColumns.php create mode 100644 src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/AddLocalizedNamesToProduct.php create mode 100644 src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/UpdateAttachmentFieldConfigForProductImage.php create mode 100755 src/Marello/Bundle/ProductBundle/Resources/config/jsmodules.yml delete mode 100755 src/Marello/Bundle/ProductBundle/Resources/config/requirejs.yml delete mode 100644 src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultChannelPrices.html.twig delete mode 100644 src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultPrices.html.twig delete mode 100644 src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/msrpPrices.html.twig delete mode 100644 src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialChannelPrices.html.twig delete mode 100644 src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialPrices.html.twig create mode 100644 src/Marello/Bundle/ProductBundle/Twig/DynamicFieldsExtension.php create mode 100644 src/Marello/Bundle/ProductBundle/Validator/Constraints/ProductSupplierRelationsDropship.php create mode 100644 src/Marello/Bundle/ProductBundle/Validator/ProductSupplierRelationsDropshipValidator.php create mode 100644 src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_2/MarelloPurchaseOrderBundle.php create mode 100644 src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_3/MarelloPurchaseOrderBundle.php create mode 100644 src/Marello/Bundle/PurchaseOrderBundle/Resources/config/jsmodules.yml delete mode 100644 src/Marello/Bundle/PurchaseOrderBundle/Resources/config/requirejs.yml create mode 100644 src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/components/purchaseorder-product-component.js create mode 100644 src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_3/MarelloRefundBundle.php create mode 100644 src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_4/MarelloReturnBundle.php create mode 100644 src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleAddIsSystemColumn.php create mode 100644 src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleDropSystemColumn.php create mode 100644 src/Marello/Bundle/RuleBundle/Validator/Constraints/ExpressionLanguageSyntax.php create mode 100644 src/Marello/Bundle/RuleBundle/Validator/Constraints/ExpressionLanguageSyntaxValidator.php create mode 100644 src/Marello/Bundle/SalesBundle/Config/SalesChannelScopeManager.php create mode 100644 src/Marello/Bundle/SalesBundle/Controller/ConfigController.php create mode 100644 src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelGridListener.php create mode 100644 src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelsDatagridListener.php create mode 100644 src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundle.php create mode 100644 src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleAddIsSystemColumn.php create mode 100644 src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleDropSystemColumn.php delete mode 100644 src/Marello/Bundle/SalesBundle/Model/ChannelAwareInterface.php create mode 100644 src/Marello/Bundle/SalesBundle/Model/SalesChannelsAwareInterface.php create mode 100644 src/Marello/Bundle/SalesBundle/Placeholder/PlaceholderFilter.php create mode 100644 src/Marello/Bundle/SalesBundle/Provider/SalesChannelConfigurationFormProvider.php create mode 100755 src/Marello/Bundle/SalesBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/SalesBundle/Resources/config/oro/placeholders.yml delete mode 100755 src/Marello/Bundle/SalesBundle/Resources/config/requirejs.yml create mode 100644 src/Marello/Bundle/SalesBundle/Resources/views/Config/button.html.twig create mode 100644 src/Marello/Bundle/SalesBundle/Resources/views/Config/salesChannel.html.twig rename src/Marello/Bundle/{ProductBundle/Resources/views/Product => SalesBundle/Resources/views/SalesChannel}/Datagrid/Property/channels.html.twig (100%) delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/AllowUnlistedShippingMethodConfigurationInterface.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfiguration.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilder.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilderFactory.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilderFactoryInterface.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilderInterface.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationInterface.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/MethodLockedShippingMethodConfigurationInterface.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/OverriddenCostShippingMethodConfigurationInterface.php delete mode 100644 src/Marello/Bundle/ShippingBundle/Method/Configuration/PreConfiguredShippingMethodConfigurationInterface.php create mode 100755 src/Marello/Bundle/ShippingBundle/Resources/config/jsmodules.yml delete mode 100755 src/Marello/Bundle/ShippingBundle/Resources/config/requirejs.yml create mode 100644 src/Marello/Bundle/ShippingBundle/Resources/config/validation.yml create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/Controller/ShippingMethodsConfigsRuleControllerTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodConfigsWithFakeMethods.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodTypeConfigsWithFakeTypes.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRules.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRulesWithConfigs.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadUserData.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_configs_with_fake_methods.yml create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_type_configs_with_fake_types.yml create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules.yml create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules_with_configs.yml create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodConfigRepositoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodTypeConfigRepositoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodsConfigsRuleRepositoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Functional/Helper/ManualShippingIntegrationTrait.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingMethodEnabledByIdentifierCheckerTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingRuleEnabledCheckerTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/HasApplicableShippingMethodsTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ShippingMethodHasShippingRulesTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ToStringStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/AbstractShippingLineItemTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/BasicShippingContextBuilderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicShippingContextBuilderFactoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicShippingLineItemBuilderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicLineItemBuilderByLineItemFactoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicShippingLineItemBuilderFactoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionFactoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextCacheKeyGeneratorTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextMockTrait.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingLineItemTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Converter/Basic/ShippingContextToRuleValuesConverterTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodConfigTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodTypeConfigTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleDestinationPostalCodeTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleDestinationTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleMockTrait.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/CompositeShippingMethodProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodTypeRemovalEventDispatcherTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodTypeRemovalEventTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/ShippingMethodDisableIntegrationListenerTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Handler/RulesShippingMethodDisableHandlerDecoratorTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Integration/ChannelShippingMethodProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Label/Type/BasicMethodTypeLabelsProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Type/NonDeletable/ShippingRulesNonDeletableMethodTypeIdentifiersProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewCollectionTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewFactoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Stub/TrackingAwareShippingMethodStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/TrackingAwareShippingMethodsProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Basic/BasicShippingMethodValidatorTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/EnabledShippingMethodsByRules/EnabledShippingMethodsByRulesShippingMethodValidatorDecoratorTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Builder/Common/Doctrine/DoctrineCommonShippingMethodValidatorResultErrorCollectionBuilderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Doctrine/DoctrineShippingMethodValidatorResultErrorCollectionTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultErrorFactoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/ParameterBag/ParameterBagShippingMethodValidatorResultErrorTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultFactoryTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/ParameterBag/ParameterBagShippingMethodValidatorResultTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/BasicShippingMethodChoicesProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Cache/ShippingPriceCacheTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledMethodsShippingPriceProviderDecoratorTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledShippingMethodChoicesProviderDecoratorTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/PriceAwareShippingMethodStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingAddressStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodProviderStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeConfigTypeOptionsStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeStub.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/test/ShippingPriceProviderTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php create mode 100644 src/Marello/Bundle/ShippingBundle/Tests/Unit/Tools/FilteredDatagridRouteHelperTest.php create mode 100644 src/Marello/Bundle/SupplierBundle/Resources/config/jsmodules.yml create mode 100644 src/Marello/Bundle/SupplierBundle/Resources/config/oro/twig.yml create mode 100644 src/Marello/Bundle/TaxBundle/EventListener/Datagrid/TaxCodeDatagridListener.php rename src/Marello/Bundle/TaxBundle/Resources/config/{service.yml => services.yml} (86%) create mode 100644 src/Marello/Bundle/UPSBundle/Resources/config/jsmodules.yml delete mode 100644 src/Marello/Bundle/UPSBundle/Resources/config/requirejs.yml diff --git a/README.md b/README.md index 0b259c518..8aac9b9fe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ Marello ======================== -[![GitHub release](https://img.shields.io/github/release/marellocommerce/marello.svg)](https://github.com/marellocommerce/marello/releases) What is Marello? ----------- @@ -25,7 +24,7 @@ Use as dependency in composer Run unit tests -------------- -Please make sure you have at least phpunit 6.5 or above. +Please make sure you have at least phpunit 7.5 or above. To run unit tests of any bundles: ```bash diff --git a/composer.json b/composer.json index dd3e7bd8d..d57ff137c 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "license": "OSL-3.0", "authors": [ { - "name": "Madia B.V.", + "name": "Marello B.V.", "homepage": "https://www.marello.com" } ], @@ -20,16 +20,16 @@ } }, "require": { - "php": ">=7.1.26", - "oro/platform": "~3.1.1", - "oro/platform-serialised-fields": "~3.1.1", - "oro/calendar-bundle": "~3.1.1" + "oro/platform": "4.1.*", + "oro/platform-serialised-fields": "4.1.*", + "oro/calendar-bundle": "4.1.*", + "mpdf/mpdf": "^7.1" }, "minimum-stability": "dev", "prefer-stable": true, "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "3.0-dev" } }, "autoload": { diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 000000000..1ca997513 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,10 @@ + + + PSR2 ruleset with Marello configuration + + */marellocommerce/*/*/*/*/*/Tests/* + */marellocommerce/*/*/*/*/*/Migrations/Schema/* + + + + diff --git a/src/Marello/Bundle/AddressBundle/Entity/MarelloAddress.php b/src/Marello/Bundle/AddressBundle/Entity/MarelloAddress.php index 40f8c01c8..d0cfc166c 100644 --- a/src/Marello/Bundle/AddressBundle/Entity/MarelloAddress.php +++ b/src/Marello/Bundle/AddressBundle/Entity/MarelloAddress.php @@ -4,11 +4,13 @@ use Doctrine\ORM\Mapping as ORM; use Marello\Bundle\AddressBundle\Model\ExtendMarelloAddress; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; /** - * @ORM\Entity + * @ORM\Entity( + * repositoryClass="Marello\Bundle\AddressBundle\Entity\Repository\MarelloAddressRepository" + * ) * @ORM\HasLifecycleCallbacks() * @ORM\Table(name="marello_address") * @ORM\AssociationOverrides({ @@ -62,7 +64,7 @@ class MarelloAddress extends ExtendMarelloAddress protected $company; /** - * @ORM\ManyToOne(targetEntity="Marello\Bundle\OrderBundle\Entity\Customer", inversedBy="addresses", + * @ORM\ManyToOne(targetEntity="Marello\Bundle\CustomerBundle\Entity\Customer", inversedBy="addresses", * cascade={"persist"}) * @ORM\JoinColumn(onDelete="SET NULL", nullable=true) * diff --git a/src/Marello/Bundle/AddressBundle/Entity/Repository/MarelloAddressRepository.php b/src/Marello/Bundle/AddressBundle/Entity/Repository/MarelloAddressRepository.php new file mode 100644 index 000000000..2699b5b51 --- /dev/null +++ b/src/Marello/Bundle/AddressBundle/Entity/Repository/MarelloAddressRepository.php @@ -0,0 +1,74 @@ +aclHelper = $aclHelper; + } + + /** + * @param MarelloAddress $address + * + * @return MarelloEnterpriseAddress[] + */ + public function findByAddressParts(MarelloAddress $address) + { + $qb = $this->createQueryBuilder('addr'); + $qb + ->innerJoin('addr.country', 'country') + ->andWhere($qb->expr()->eq('country.iso2Code', ':countryIso2')) + ->andWhere($qb->expr()->eq('addr.city', ':city')) + ->andWhere($qb->expr()->eq('addr.street', ':street')) + ->setParameter('countryIso2', $address->getCountryIso2()) + ->setParameter('city', $address->getCity()) + ->setParameter('street', $address->getStreet()); + + if ($address->getRegion() === null) { + $qb->andWhere('addr.region IS NULL'); + } else { + $qb + ->innerJoin('addr.region', 'region') + ->andWhere($qb->expr()->eq('region.code', ':regionCode')) + ->setParameter('regionCode', $address->getRegionCode()); + } + if ($address->getRegionText() === null) { + $qb->andWhere('addr.regionText IS NULL'); + } else { + $qb + ->andWhere($qb->expr()->eq('addr.regionText', ':regionText')) + ->setParameter('regionText', $address->getRegionText()); + } + if ($address->getStreet2() === null) { + $qb->andWhere('addr.street2 IS NULL'); + } else { + $qb + ->andWhere($qb->expr()->eq('addr.street2', ':street2')) + ->setParameter('street2', $address->getStreet2()); + } + if ($address->getPostalCode() === null) { + $qb->andWhere('addr.postalCode IS NULL'); + } else { + $qb + ->andWhere($qb->expr()->eq('addr.postalCode', ':postalCode')) + ->setParameter('postalCode', $address->getPostalCode()); + } + + return $this->aclHelper->apply($qb)->getResult(); + } +} diff --git a/src/Marello/Bundle/AddressBundle/Migrations/Schema/v1_1/MarelloAddressBundle.php b/src/Marello/Bundle/AddressBundle/Migrations/Schema/v1_1/MarelloAddressBundle.php index 943e3edbe..9cf8775eb 100644 --- a/src/Marello/Bundle/AddressBundle/Migrations/Schema/v1_1/MarelloAddressBundle.php +++ b/src/Marello/Bundle/AddressBundle/Migrations/Schema/v1_1/MarelloAddressBundle.php @@ -25,6 +25,7 @@ public function up(Schema $schema, QueryBag $queries) * Update marello_address table * * @param Schema $schema + * @param QueryBag $queries */ protected function updateDatetimeMarelloAddressTable(Schema $schema, QueryBag $queries) { diff --git a/src/Marello/Bundle/AddressBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/AddressBundle/Resources/config/jsmodules.yml new file mode 100644 index 000000000..831b0ef12 --- /dev/null +++ b/src/Marello/Bundle/AddressBundle/Resources/config/jsmodules.yml @@ -0,0 +1,4 @@ +dynamic-imports: + marelloaddress: + - marelloaddress/js/address + - marelloaddress/js/app/components/address-component \ No newline at end of file diff --git a/src/Marello/Bundle/AddressBundle/Resources/config/oro/placeholders.yml b/src/Marello/Bundle/AddressBundle/Resources/config/oro/placeholders.yml new file mode 100644 index 000000000..61c7980c0 --- /dev/null +++ b/src/Marello/Bundle/AddressBundle/Resources/config/oro/placeholders.yml @@ -0,0 +1,10 @@ +placeholders: + placeholders: + marello_address_map: + items: + marello_address_item_map: + order: 100 + + items: + marello_address_item_map: + template: MarelloAddressBundle:Address:addressMap.html.twig \ No newline at end of file diff --git a/src/Marello/Bundle/AddressBundle/Resources/config/requirejs.yml b/src/Marello/Bundle/AddressBundle/Resources/config/requirejs.yml deleted file mode 100644 index 07f022bca..000000000 --- a/src/Marello/Bundle/AddressBundle/Resources/config/requirejs.yml +++ /dev/null @@ -1,3 +0,0 @@ -config: - paths: - 'marelloaddress/js/address': 'bundles/marelloaddress/js/address.js' \ No newline at end of file diff --git a/src/Marello/Bundle/AddressBundle/Resources/config/services.yml b/src/Marello/Bundle/AddressBundle/Resources/config/services.yml index f018d5435..d51785a5d 100644 --- a/src/Marello/Bundle/AddressBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/AddressBundle/Resources/config/services.yml @@ -1,4 +1,3 @@ - services: marello_address.formatter.address: class: Marello\Bundle\AddressBundle\Formatter\AddressFormatter @@ -13,4 +12,12 @@ services: arguments: - '@marello_address.formatter.address' tags: - - { name: twig.extension } \ No newline at end of file + - { name: twig.extension } + + marello_address.repository.marelloaddress: + class: 'Marello\Bundle\AddressBundle\Entity\Repository\MarelloAddressRepository' + parent: oro_entity.abstract_repository + arguments: + - 'Marello\Bundle\AddressBundle\Entity\MarelloAddress' + calls: + - [setAclHelper, ['@oro_security.acl_helper']] \ No newline at end of file diff --git a/src/Marello/Bundle/AddressBundle/Resources/public/js/address.js b/src/Marello/Bundle/AddressBundle/Resources/public/js/address.js index dd2ce1080..9a8931ddb 100644 --- a/src/Marello/Bundle/AddressBundle/Resources/public/js/address.js +++ b/src/Marello/Bundle/AddressBundle/Resources/public/js/address.js @@ -1,32 +1,28 @@ define([ 'underscore', - 'backbone', + 'oroui/js/app/views/base/view', 'orotranslation/js/translator', 'oroui/js/mediator', 'oroui/js/messenger', - 'oro/dialog-widget', - 'oroaddress/js/address/view' + 'oro/dialog-widget' ], function( _, - Backbone, + BaseView, __, mediator, messenger, - DialogWidget, - AddressView + DialogWidget ) { 'use strict'; - var $ = Backbone.$; - /** * @export marelloaddress/js/address * @class marelloaddress.Address * @extends Backbone.View */ - return Backbone.View.extend({ + const AddressView = BaseView.extend({ options: { - 'addressUpdateUrl': null, + 'addressUpdateUrl': null }, initialize: function(options) { @@ -77,6 +73,7 @@ define([ } }, this) ); + this.addressEditDialog.on('formSave', _.bind(function() { this.addressEditDialog.remove(); messenger.notificationFlashMessage('success', __('Address saved')); @@ -87,6 +84,8 @@ define([ reloadAddress: function() { this.getAddressWidget().render(); - }, + } }); + + return AddressView; }); diff --git a/src/Marello/Bundle/AddressBundle/Resources/public/js/app/components/address-component.js b/src/Marello/Bundle/AddressBundle/Resources/public/js/app/components/address-component.js new file mode 100644 index 000000000..b58418efa --- /dev/null +++ b/src/Marello/Bundle/AddressBundle/Resources/public/js/app/components/address-component.js @@ -0,0 +1,48 @@ +define(function(require) { + 'use strict'; + + const BaseComponent = require('oroui/js/app/components/base/component'); + const widgetManager = require('oroui/js/widget-manager'); + const Address = require('marelloaddress/js/address'); + const routing = require('routing'); + const _ = require('underscore'); + + const AddressWidgetComponent = BaseComponent.extend({ + optionNames: BaseComponent.prototype.optionNames.concat([ + 'wid', 'addressCreateUrl', 'addressUpdateRoute', 'addressDeleteRoute' + ]), + + options: null, + + /** + * @inheritDoc + */ + constructor: function AddressWidgetComponent(options) { + AddressWidgetComponent.__super__.constructor.call(this, options); + }, + + /** + * @inheritDoc + */ + initialize: function(options) { + this.options = options; + AddressWidgetComponent.__super__.initialize.call(this, options); + widgetManager.getWidgetInstance(this.wid, this._initializeAddress.bind(this)); + }, + + _initializeAddress: function(widget) { + const options = this.options; + return new Address({ + el: this.options.el, + addressId: this.options.addressId, + addressUpdateUrl: function() { + return routing.generate(options.addressUpdateRoute.route, {'id': options.addressUpdateRoute.id }) + }, + widget: widget + }); + } + + }); + + return AddressWidgetComponent; +}); \ No newline at end of file diff --git a/src/Marello/Bundle/OrderBundle/Resources/views/Customer/customerAddressMap.html.twig b/src/Marello/Bundle/AddressBundle/Resources/views/Address/addressMap.html.twig similarity index 91% rename from src/Marello/Bundle/OrderBundle/Resources/views/Customer/customerAddressMap.html.twig rename to src/Marello/Bundle/AddressBundle/Resources/views/Address/addressMap.html.twig index 7ec3ab3eb..47a54a647 100644 --- a/src/Marello/Bundle/OrderBundle/Resources/views/Customer/customerAddressMap.html.twig +++ b/src/Marello/Bundle/AddressBundle/Resources/views/Address/addressMap.html.twig @@ -1,5 +1,5 @@ {% set apiKey = oro_config_value('oro_google_integration.google_api_key') %} -{% if apiKey is not empty%} +{% if apiKey is not empty and address is not null %}
labels = new ArrayCollection(); + } + + /** + * @return Collection|LocalizedFallbackValue[] + */ + public function getLabels() + { + return $this->labels; + } + + /** + * @param LocalizedFallbackValue $label + * + * @return $this + */ + public function addLabel(LocalizedFallbackValue $label) + { + if (!$this->labels->contains($label)) { + $this->labels->add($label); + } + + return $this; + } + + /** + * @param LocalizedFallbackValue $label + * + * @return $this + */ + public function removeLabel(LocalizedFallbackValue $label) + { + if ($this->labels->contains($label)) { + $this->labels->removeElement($label); + } + + return $this; + } + + /** + * @return ParameterBag + */ + public function getSettingsBag() + { + if (null === $this->settings) { + $this->settings = new ParameterBag( + [ + 'labels' => $this->getLabels()->toArray(), + ] + ); + } + + return $this->settings; + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Entity/Repository/BankTransferSettingsRepository.php b/src/Marello/Bundle/BankTransferBundle/Entity/Repository/BankTransferSettingsRepository.php new file mode 100644 index 000000000..50467b325 --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Entity/Repository/BankTransferSettingsRepository.php @@ -0,0 +1,38 @@ +aclHelper = $aclHelper; + } + + /** + * @return BankTransferSettings[] + */ + public function findWithEnabledChannel() + { + $qb = $this->createQueryBuilder('bts'); + + $qb + ->join('bts.channel', 'ch') + ->where('ch.enabled = true') + ->orderBy('bts.id'); + + return $this->aclHelper->apply($qb)->getResult(); + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferOptionsType.php b/src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferOptionsType.php new file mode 100755 index 000000000..01b02b23a --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferOptionsType.php @@ -0,0 +1,48 @@ +add(BankTransferMethod::INSTRUCTIONS_OPTION, TextareaType::class, [ + 'required' => true, + 'label' => 'marello.bank_transfer.method.instructions.label', + ]); + } + + /** + * @param OptionsResolver $resolver + * + * @throws AccessException + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'label' => 'marello.bank_transfer.form.marello_bank_transfer_options_type.label', + ]); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferSettingsType.php b/src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferSettingsType.php new file mode 100644 index 000000000..141dd59a2 --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Form/Type/BankTransferSettingsType.php @@ -0,0 +1,63 @@ +add( + 'labels', + LocalizedFallbackValueCollectionType::class, + [ + 'label' => 'marello.bank_transfer.settings.labels.label', + 'required' => true, + 'entry_options' => ['constraints' => [new NotBlank()]], + ] + ); + } + + /** + * @param OptionsResolver $resolver + * + * @throws AccessException + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults( + [ + 'data_class' => BankTransferSettings::class, + ] + ); + } + + /** + * @return string + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Integration/BankTransferChannelType.php b/src/Marello/Bundle/BankTransferBundle/Integration/BankTransferChannelType.php new file mode 100644 index 000000000..5a7ddb976 --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Integration/BankTransferChannelType.php @@ -0,0 +1,27 @@ +extension) { + $this->extension = new MarelloBankTransferExtension(); + } + + return $this->extension; + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Method/BankTransferMethod.php b/src/Marello/Bundle/BankTransferBundle/Method/BankTransferMethod.php new file mode 100644 index 000000000..05cdb26cc --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Method/BankTransferMethod.php @@ -0,0 +1,102 @@ +identifier = $identifier; + $this->label = $label; + $this->icon = $icon; + $this->enabled = $enabled; + } + + /** + * {@inheritDoc} + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * {@inheritDoc} + */ + public function isEnabled() + { + return $this->enabled; + } + + /** + * {@inheritDoc} + */ + public function getLabel() + { + return $this->label; + } + + /** + * {@inheritDoc} + */ + public function getIcon() + { + return $this->icon; + } + + /** + * {@inheritDoc} + */ + public function getOptionsConfigurationFormType() + { + return BankTransferOptionsType::class; + } + + /** + * {@inheritDoc} + */ + public function getOptions() + { + return []; + } + + /** + * {@inheritDoc} + */ + public function getSortOrder() + { + return 10; + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Method/Factory/BankTransferMethodFromChannelFactory.php b/src/Marello/Bundle/BankTransferBundle/Method/Factory/BankTransferMethodFromChannelFactory.php new file mode 100644 index 000000000..8e916e57e --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Method/Factory/BankTransferMethodFromChannelFactory.php @@ -0,0 +1,82 @@ +identifierGenerator = $identifierGenerator; + $this->localizationHelper = $localizationHelper; + $this->integrationIconProvider = $integrationIconProvider; + } + + /** + * @param Channel $channel + * + * @return ManualShippingMethod + */ + public function create(Channel $channel) + { + $id = $this->identifierGenerator->generateIdentifier($channel); + $label = $this->getChannelLabel($channel); + $icon = $this->getIcon($channel); + + return new BankTransferMethod($id, $label, $icon, $channel->isEnabled()); + } + + /** + * @param Channel $channel + * + * @return string + */ + private function getChannelLabel(Channel $channel) + { + /** @var ManualShippingSettings $transport */ + $transport = $channel->getTransport(); + + return (string) $this->localizationHelper->getLocalizedValue($transport->getLabels()); + } + + /** + * @param Channel $channel + * + * @return string|null + */ + private function getIcon(Channel $channel) + { + return $this->integrationIconProvider->getIcon($channel); + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Method/Provider/BankTransferMethodProvider.php b/src/Marello/Bundle/BankTransferBundle/Method/Provider/BankTransferMethodProvider.php new file mode 100644 index 000000000..59658fd7d --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Method/Provider/BankTransferMethodProvider.php @@ -0,0 +1,21 @@ +createMarelloBankTransferTransportLabelTable($schema); + $this->addMarelloBankTransferTransportLabelForeignKeys($schema); + } + + /** + * @param Schema $schema + */ + private function createMarelloBankTransferTransportLabelTable(Schema $schema) + { + $table = $schema->createTable('marello_bank_transfer_tr_lbl'); + + $table->addColumn('transport_id', 'integer', []); + $table->addColumn('localized_value_id', 'integer', []); + + $table->setPrimaryKey(['transport_id', 'localized_value_id']); + $table->addIndex(['transport_id'], 'marello_bank_transfer_trans_label_transport_id', []); + $table->addUniqueIndex(['localized_value_id'], 'marello_bank_transfer_trans_label_localized_value_id', []); + } + + /** + * @param Schema $schema + * + * @throws SchemaException + */ + private function addMarelloBankTransferTransportLabelForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_bank_transfer_tr_lbl'); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_fallback_localization_val'), + ['localized_value_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + $table->addForeignKeyConstraint( + $schema->getTable('oro_integration_transport'), + ['transport_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Migrations/Schema/v1_0/MarelloBankTransferBundle.php b/src/Marello/Bundle/BankTransferBundle/Migrations/Schema/v1_0/MarelloBankTransferBundle.php new file mode 100644 index 000000000..7f3ea728f --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Migrations/Schema/v1_0/MarelloBankTransferBundle.php @@ -0,0 +1,59 @@ +createMarelloBankTransferTransportLabelTable($schema); + $this->addMarelloBankTransferTransportLabelForeignKeys($schema); + } + + /** + * @param Schema $schema + */ + private function createMarelloBankTransferTransportLabelTable(Schema $schema) + { + $table = $schema->createTable('marello_bank_transfer_tr_lbl'); + + $table->addColumn('transport_id', 'integer', []); + $table->addColumn('localized_value_id', 'integer', []); + + $table->setPrimaryKey(['transport_id', 'localized_value_id']); + $table->addIndex(['transport_id'], 'marello_bank_transfer_trans_label_transport_id', []); + $table->addUniqueIndex(['localized_value_id'], 'marello_bank_transfer_trans_label_localized_value_id', []); + } + + /** + * @param Schema $schema + * + * @throws SchemaException + */ + private function addMarelloBankTransferTransportLabelForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_bank_transfer_tr_lbl'); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_fallback_localization_val'), + ['localized_value_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + $table->addForeignKeyConstraint( + $schema->getTable('oro_integration_transport'), + ['transport_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } +} diff --git a/src/Marello/Bundle/BankTransferBundle/Resources/config/factory.yml b/src/Marello/Bundle/BankTransferBundle/Resources/config/factory.yml new file mode 100644 index 000000000..06d20ef3a --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Resources/config/factory.yml @@ -0,0 +1,8 @@ +services: + marello_bank_transfer.factory.method: + class: 'Marello\Bundle\BankTransferBundle\Method\Factory\BankTransferMethodFromChannelFactory' + public: false + arguments: + - '@marello_bank_transfer.method.identifier_generator.method' + - '@oro_locale.helper.localization' + - '@oro_integration.provider.integration_icon' diff --git a/src/Marello/Bundle/BankTransferBundle/Resources/config/integration.yml b/src/Marello/Bundle/BankTransferBundle/Resources/config/integration.yml new file mode 100644 index 000000000..80c012125 --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Resources/config/integration.yml @@ -0,0 +1,16 @@ +parameters: + marello_bank_transfer.integration.channel.type: 'bank_transfer' + marello_bank_transfer.integration.transport.type: 'bank_transfer' + +services: + marello_bank_transfer.integration.channel: + class: 'Marello\Bundle\BankTransferBundle\Integration\BankTransferChannelType' + public: true + tags: + - { name: oro_integration.channel, type: bank_transfer } + + marello_bank_transfer.integration.transport: + class: 'Marello\Bundle\BankTransferBundle\Integration\BankTransferTransport' + public: false + tags: + - { name: oro_integration.transport, type: bank_transfer, channel_type: bank_transfer } diff --git a/src/Marello/Bundle/BankTransferBundle/Resources/config/oro/actions.yml b/src/Marello/Bundle/BankTransferBundle/Resources/config/oro/actions.yml new file mode 100755 index 000000000..bba4c4133 --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Resources/config/oro/actions.yml @@ -0,0 +1,153 @@ +operations: + oro_integration_deactivate: + label: oro.integration.deactivate + preactions: + - '@assign_constant_value': + attribute: $.bankTransferType + value: Marello\Bundle\BankTransferBundle\Integration\BankTransferChannelType::TYPE + preconditions: + '@and': + - '@not_equal': [$type, $.bankTransferType] + + oro_integration_delete: + label: oro.integration.delete + preactions: + - '@assign_constant_value': + attribute: $.bankTransferType + value: Marello\Bundle\BankTransferBundle\Integration\BankTransferChannelType::TYPE + preconditions: + '@and': + - '@not_equal': [$type, $.bankTransferType] + + marello_bank_transfer_integration_deactivate: + label: oro.integration.deactivate + extends: oro_integration_deactivate + for_all_entities: false + for_all_datagrids: false + replace: + - preactions + - preconditions + - frontend_options + preactions: + - '@assign_constant_value': + attribute: $.bankTransferType + value: Marello\Bundle\BankTransferBundle\Integration\BankTransferChannelType::TYPE + - '@call_service_method': + attribute: $.actionAllowed + service: oro_integration.utils.edit_mode + method: isSwitchEnableAllowed + method_parameters: [$.data.editMode] + - '@call_service_method': + attribute: $.methodIdentifier + service: marello_bank_transfer.method.identifier_generator.method + method: generateIdentifier + method_parameters: [$.data] + - '@call_service_method': + attribute: $.linkGrid + service: marello_payment.helper.filtered_datagrid_route + method: generate + method_parameters: [{'methodConfigs': $.methodIdentifier}] + preconditions: + '@and': + - '@marello_payment_method_has_enabled_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.bankTransferType] + - '@equal': [$.actionAllowed, true] + - '@equal': [$.data.enabled, true] + frontend_options: + confirmation: + title: marello.payment.integration.deactivate.title + okText: marello.payment.integration.deactivate.button.okText + message: marello.payment.integration.deactivate.message + message_parameters: + linkGrid: $.linkGrid + component: oroui/js/standart-confirmation + + marello_bank_transfer_integration_deactivate_without_rules: + label: oro.integration.deactivate + extends: marello_bank_transfer_integration_deactivate + for_all_entities: false + for_all_datagrids: false + replace: + - preconditions + - frontend_options + preconditions: + '@and': + - '@not': + - '@marello_payment_method_has_enabled_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.bankTransferType] + - '@equal': [$.actionAllowed, true] + - '@equal': [$.data.enabled, true] + frontend_options: ~ + + marello_bank_transfer_integration_delete: + label: oro.integration.delete + extends: oro_integration_delete + for_all_entities: false + for_all_datagrids: false + replace: + - preactions + - preconditions + - frontend_options + preactions: + - '@assign_constant_value': + attribute: $.bankTransferType + value: Marello\Bundle\BankTransferBundle\Integration\BankTransferChannelType::TYPE + - '@call_service_method': + service: oro_integration.utils.edit_mode + method: isEditAllowed + method_parameters: [$.data.editMode] + attribute: $.actionAllowed + - '@call_service_method': + attribute: $.methodIdentifier + service: marello_bank_transfer.method.identifier_generator.method + method: generateIdentifier + method_parameters: [$.data] + - '@call_service_method': + attribute: $.linkGrid + service: marello_payment.helper.filtered_datagrid_route + method: generate + method_parameters: [{'methodConfigs': $.methodIdentifier}] + preconditions: + '@and': + - '@marello_payment_method_has_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.bankTransferType] + - '@equal': [$.actionAllowed, true] + frontend_options: + confirmation: + title: marello.payment.integration.delete.title + okText: marello.payment.integration.delete.button.okText + message: marello.payment.integration.delete.message + message_parameters: + linkGrid: $.linkGrid + component: oroui/js/standart-confirmation + + marello_bank_transfer_integration_delete_without_rules: + label: oro.integration.delete + extends: marello_bank_transfer_integration_delete + for_all_entities: false + for_all_datagrids: false + replace: + - preconditions + - frontend_options + preconditions: + '@and': + - '@not': + - '@marello_payment_method_has_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.bankTransferType] + - '@equal': [$.actionAllowed, true] + frontend_options: + title: oro.action.delete_entity + confirmation: + title: oro.action.delete_entity + message: oro.action.delete_confirm + message_parameters: + entityLabel: $name + component: oroui/js/delete-confirmation diff --git a/src/Marello/Bundle/BankTransferBundle/Resources/config/oro/bundles.yml b/src/Marello/Bundle/BankTransferBundle/Resources/config/oro/bundles.yml new file mode 100644 index 000000000..08499145a --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Resources/config/oro/bundles.yml @@ -0,0 +1,2 @@ +bundles: + - { name: Marello\Bundle\BankTransferBundle\MarelloBankTransferBundle, priority: 40 } diff --git a/src/Marello/Bundle/BankTransferBundle/Resources/config/payment.yml b/src/Marello/Bundle/BankTransferBundle/Resources/config/payment.yml new file mode 100644 index 000000000..e3f1545c1 --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Resources/config/payment.yml @@ -0,0 +1,11 @@ +services: + marello_bank_transfer.payment_method_provider.bank_transfer: + class: 'Marello\Bundle\BankTransferBundle\Method\Provider\BankTransferMethodProvider' + public: false + arguments: + - '%marello_bank_transfer.integration.channel.type%' + - '@oro_entity.doctrine_helper' + - '@marello_bank_transfer.factory.method' + tags: + - { name: marello_payment.payment_method_provider } + - { name: doctrine.orm.entity_listener, entity: 'Oro\Bundle\IntegrationBundle\Entity\Channel', event: postLoad } \ No newline at end of file diff --git a/src/Marello/Bundle/BankTransferBundle/Resources/config/services.yml b/src/Marello/Bundle/BankTransferBundle/Resources/config/services.yml new file mode 100644 index 000000000..ec26737ed --- /dev/null +++ b/src/Marello/Bundle/BankTransferBundle/Resources/config/services.yml @@ -0,0 +1,31 @@ +parameters: + marello_bank_transfer.method.identifier_prefix.bank_transfer: 'bank_transfer' + marello_bank_transfer.admin_view.method_template: 'MarelloBankTransferBundle::method/bankTransferMethodWithOptions.html.twig' + +services: + marello_bank_transfer.method.identifier_generator.method: + parent: oro_integration.generator.prefixed_identifier_generator + public: true + arguments: + - '%marello_bank_transfer.method.identifier_prefix.bank_transfer%' + + marello_bank_transfer.repository.bank_transfer_settings: + class: 'Marello\Bundle\BankTransferBundle\Entity\Repository\BankTransferSettingsRepository' + parent: oro_entity.abstract_repository + arguments: + - 'Marello\Bundle\BankTransferBundle\Entity\BankTransferSettings' + calls: + - [setAclHelper, ['@oro_security.acl_helper']] + + marello_bank_transfer.form.type.bank_transfer_options: + class: 'Marello\Bundle\BankTransferBundle\Form\Type\BankTransferOptionsType' + tags: + - { name: form.type } + + marello_bank_transfer.event_listener.payment_method_config_data: + parent: marello_payment.admin_view.method_template.listener + arguments: + - '%marello_bank_transfer.admin_view.method_template%' + - '@marello_bank_transfer.payment_method_provider.bank_transfer' + tags: + - { name: kernel.event_listener, event: marello_payment_method.config_data, method: onGetConfigData } diff --git a/src/Marello/Bundle/BankTransferBundle/Resources/public/img/bank-transfer-icon.png b/src/Marello/Bundle/BankTransferBundle/Resources/public/img/bank-transfer-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3f0a8bfaec3f2d0ba9e5ae8c87f2592fd6291037 GIT binary patch literal 5253 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 + {{ 'marello.bank_transfer.method.instructions.label'|trans }}: {{ methodData.options.instructions }} +{% endspaceless %} diff --git a/src/Marello/Bundle/CatalogBundle/Controller/CategoryController.php b/src/Marello/Bundle/CatalogBundle/Controller/CategoryController.php index a992c407b..c152d9e30 100644 --- a/src/Marello/Bundle/CatalogBundle/Controller/CategoryController.php +++ b/src/Marello/Bundle/CatalogBundle/Controller/CategoryController.php @@ -4,15 +4,16 @@ use Marello\Bundle\CatalogBundle\Entity\Category; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Routing\Annotation\Route; -class CategoryController extends Controller +class CategoryController extends AbstractController { /** - * @Config\Route("/", name="marello_category_index") + * @Route(path="/", name="marello_category_index") * @AclAncestor("marello_category_view") - * @Config\Template + * @Template */ public function indexAction() { @@ -20,9 +21,9 @@ public function indexAction() } /** - * @Config\Route("/create", name="marello_category_create") + * @Route(path="/create", name="marello_category_create") * @AclAncestor("marello_category_create") - * @Config\Template("MarelloCatalogBundle:Category:update.html.twig") + * @Template("MarelloCatalogBundle:Category:update.html.twig") * * @return array */ @@ -32,9 +33,9 @@ public function createAction() } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_category_update") + * @Route(path="/update/{id}", requirements={"id"="\d+"}, name="marello_category_update") * @AclAncestor("marello_category_update") - * @Config\Template("MarelloCatalogBundle:Category:update.html.twig") + * @Template("MarelloCatalogBundle:Category:update.html.twig") * * @param Category $category * @@ -70,9 +71,9 @@ protected function update(Category $category) } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_category_view") + * @Route(path="/view/{id}", requirements={"id"="\d+"}, name="marello_category_view") * @AclAncestor("marello_category_view") - * @Config\Template("MarelloCatalogBundle:Category:view.html.twig") + * @Template("MarelloCatalogBundle:Category:view.html.twig") * * @param Category $category * diff --git a/src/Marello/Bundle/CatalogBundle/EventListener/Datagrid/CategoriesDatagridListener.php b/src/Marello/Bundle/CatalogBundle/EventListener/Datagrid/CategoriesDatagridListener.php new file mode 100644 index 000000000..60b5231d3 --- /dev/null +++ b/src/Marello/Bundle/CatalogBundle/EventListener/Datagrid/CategoriesDatagridListener.php @@ -0,0 +1,162 @@ +relatedEntityClass = $relatedEntityClass; + $this->categoriesChoicesProvider = $categoriesChoicesProvider; + + $this->expressionBuilder = new Expr(); + } + + /** + * @param BuildBefore $event + */ + public function onBuildBefore(BuildBefore $event) + { + $config = $event->getConfig(); + + $this->addSelect($config); + $this->addJoin($config); + $this->addColumn($config); + $this->addSorter($config); + $this->addFilter($config); + } + + /** + * @param DatagridConfiguration $configuration + * @return string + * @throws \InvalidArgumentException when a root entity not found in the grid + */ + protected function getAlias(DatagridConfiguration $configuration) + { + $rootAlias = $configuration->getOrmQuery()->getRootAlias(); + if (!$rootAlias) { + throw new \InvalidArgumentException( + sprintf( + 'A root entity is missing for grid "%s"', + $configuration->getName() + ) + ); + } + + return $rootAlias; + } + + /** + * @return string + */ + protected function getColumnLabel() + { + return 'marello.catalog.category.entity_plural_label'; + } + + /** + * @return string + */ + protected function getDataName() + { + return self::DATA_NAME; + } + + /** + * @return string + */ + protected function getJoinAlias() + { + return self::JOIN_ALIAS; + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSelect(DatagridConfiguration $config) + { + $config->getOrmQuery()->addSelect( + sprintf('count(%s.code) AS categoriesCount', $this->getJoinAlias()) + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addJoin(DatagridConfiguration $config) + { + $config->getOrmQuery()->addLeftJoin( + $this->getAlias($config).'.categories', + $this->getJoinAlias() + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addColumn(DatagridConfiguration $config) + { + $config->offsetSetByPath(sprintf('[columns][%s]', $this->getDataName()), [ + 'label' => $this->getColumnLabel(), + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloCatalogBundle:Datagrid/Property:categories.html.twig', + 'renderable' => false + ]); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSorter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[sorters][columns][%s]', $this->getDataName()), + ['data_name' => 'categoriesCount'] + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addFilter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', $this->getDataName()), + [ + 'type' => 'choice', + 'data_name' => $this->getJoinAlias() . '.code', + 'enabled' => false, + 'options' => [ + 'field_options' => [ + 'multiple' => true, + 'choices' => $this->categoriesChoicesProvider->getCategories() + ] + ] + ] + ); + } +} diff --git a/src/Marello/Bundle/CatalogBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/CatalogBundle/Resources/config/oro/datagrids.yml index d3e27d9a4..da0332ab3 100644 --- a/src/Marello/Bundle/CatalogBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/CatalogBundle/Resources/config/oro/datagrids.yml @@ -35,7 +35,7 @@ datagrids: createdAt: { data_name: c.createdAt } updatedAt: { data_name: c.updatedAt } default: - name: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + name: 'ASC' properties: id: ~ view_link: @@ -56,7 +56,7 @@ datagrids: marello-category-products-base-grid: acl_resource: marello_product_update - extended_entity_name: '%marello_product.entity.class%' + extended_entity_name: Marello\Bundle\ProductBundle\Entity\Product source: type: orm query: @@ -67,7 +67,7 @@ datagrids: - product.createdAt - product.updatedAt from: - - { table: '%marello_product.entity.class%', alias: product } + - { table: MarelloProductBundle:Product, alias: product } columns: sku: label: marello.product.sku.label @@ -119,7 +119,7 @@ datagrids: - products sorters: default: - sku: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + sku: 'DESC' marello-category-products-extend-grid: extends: marello-category-products-base-grid @@ -141,7 +141,7 @@ datagrids: join: left: - - join: '%marello_catalog.category.entity.class%' + join: Marello\Bundle\CatalogBundle\Entity\Category alias: productCategory conditionType: WITH condition: 'productCategory = :category_id' @@ -163,7 +163,7 @@ datagrids: inCategory: data_name: inCategory default: - inCategory: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + inCategory: 'DESC' filters: columns: inCategory: diff --git a/src/Marello/Bundle/CatalogBundle/Resources/config/services.yml b/src/Marello/Bundle/CatalogBundle/Resources/config/services.yml index 12ebf8d28..a4ccf54b1 100644 --- a/src/Marello/Bundle/CatalogBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/CatalogBundle/Resources/config/services.yml @@ -1,5 +1,3 @@ -parameters: - marello_catalog.category.entity.class: Marello\Bundle\CatalogBundle\Entity\Category services: marello_catalog.category.form.type: class: 'Marello\Bundle\CatalogBundle\Form\Type\CategoryType' @@ -16,12 +14,20 @@ services: marello_catalog.category.form.handler: class: 'Marello\Bundle\CatalogBundle\Form\Handler\CategoryHandler' - scope: request + public: true arguments: - '@marello_catalog.category.form' - '@request_stack' - '@doctrine.orm.entity_manager' + marello_catalog.event_listener.datagrid.products_grid: + class: 'Marello\Bundle\CatalogBundle\EventListener\Datagrid\CategoriesDatagridListener' + arguments: + - 'Marello\Bundle\ProductBundle\Entity\Product' + - '@marello_catalog.provider.categories_choices' + tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-products-grid, method: onBuildBefore } + marello_catalog.listener.datagrid.category_products_column_listener: class: 'Marello\Bundle\CatalogBundle\EventListener\Datagrid\CategoryProductsColumnListener' arguments: @@ -34,6 +40,7 @@ services: marello_catalog.provider.categories_choices: class: 'Marello\Bundle\CatalogBundle\Provider\CategoriesChoicesProvider' + public: true arguments: - '@oro_entity.doctrine_helper' diff --git a/src/Marello/Bundle/CatalogBundle/Resources/views/Category/index.html.twig b/src/Marello/Bundle/CatalogBundle/Resources/views/Category/index.html.twig index 4a3cbdf6b..05149af24 100644 --- a/src/Marello/Bundle/CatalogBundle/Resources/views/Category/index.html.twig +++ b/src/Marello/Bundle/CatalogBundle/Resources/views/Category/index.html.twig @@ -5,7 +5,7 @@ {% set gridName = 'marello-categories-grid' %} {% block navButtons %} - {% if resource_granted('marello_category_create') %} + {% if is_granted('marello_category_create') %} {{ UI.addButton({ 'path': path('marello_category_create'), 'entity_label': 'marello.catalog.category.entity_label'|trans diff --git a/src/Marello/Bundle/CatalogBundle/Resources/views/Category/update.html.twig b/src/Marello/Bundle/CatalogBundle/Resources/views/Category/update.html.twig index 4d122324c..2fc35db80 100644 --- a/src/Marello/Bundle/CatalogBundle/Resources/views/Category/update.html.twig +++ b/src/Marello/Bundle/CatalogBundle/Resources/views/Category/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} @@ -12,9 +13,9 @@ {{ parent() }} {{ UI.cancelButton(path('marello_category_index')) }} - {% if resource_granted('marello_category_update') %} + {% if is_granted('marello_category_update') %} {% set html = '' %} - {% if resource_granted('marello_category_view') %} + {% if is_granted('marello_category_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_category_view', 'params': {'id': '$id'} @@ -62,7 +63,7 @@ }]} ] %} - {% if resource_granted('marello_category_update') %} + {% if is_granted('marello_category_update') %} {% set dataBlocks = dataBlocks|merge([{ 'title' : 'marello.product.entity_plural_label'|trans, 'subblocks': [ diff --git a/src/Marello/Bundle/CatalogBundle/Resources/views/Category/view.html.twig b/src/Marello/Bundle/CatalogBundle/Resources/views/Category/view.html.twig index 362231de0..6e5fe02dc 100644 --- a/src/Marello/Bundle/CatalogBundle/Resources/views/Category/view.html.twig +++ b/src/Marello/Bundle/CatalogBundle/Resources/views/Category/view.html.twig @@ -1,5 +1,6 @@ {% extends 'OroUIBundle:actions:view.html.twig' %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% import 'OroEntityConfigBundle::macros.html.twig' as entityConfig %} {% oro_title_set({params : {"%name%": entity.name } }) %} diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/categories.html.twig b/src/Marello/Bundle/CatalogBundle/Resources/views/Datagrid/Property/categories.html.twig similarity index 100% rename from src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/categories.html.twig rename to src/Marello/Bundle/CatalogBundle/Resources/views/Datagrid/Property/categories.html.twig diff --git a/src/Marello/Bundle/CatalogBundle/Tests/Functional/DataFixtures/LoadCategoryData.php b/src/Marello/Bundle/CatalogBundle/Tests/Functional/DataFixtures/LoadCategoryData.php index 3fe7c6e9c..d043ae7a8 100644 --- a/src/Marello/Bundle/CatalogBundle/Tests/Functional/DataFixtures/LoadCategoryData.php +++ b/src/Marello/Bundle/CatalogBundle/Tests/Functional/DataFixtures/LoadCategoryData.php @@ -5,17 +5,9 @@ use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; - use Marello\Bundle\CatalogBundle\Entity\Category; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\ProductBundle\Tests\Functional\DataFixtures\LoadProductData; -use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; - -use Marello\Bundle\OrderBundle\Entity\OrderItem; -use Marello\Bundle\ReturnBundle\Entity\ReturnEntity; -use Marello\Bundle\ReturnBundle\Entity\ReturnItem; -use Marello\Bundle\OrderBundle\Tests\Functional\DataFixtures\LoadOrderData; -use Marello\Bundle\SalesBundle\Tests\Functional\DataFixtures\LoadSalesData; class LoadCategoryData extends AbstractFixture implements DependentFixtureInterface { diff --git a/src/Marello/Bundle/CoreBundle/Mailer/Processor.php b/src/Marello/Bundle/CoreBundle/Mailer/Processor.php index 2dc46991e..a8b0bb86e 100644 --- a/src/Marello/Bundle/CoreBundle/Mailer/Processor.php +++ b/src/Marello/Bundle/CoreBundle/Mailer/Processor.php @@ -2,18 +2,13 @@ namespace Marello\Bundle\CoreBundle\Mailer; -use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; - -use Oro\Bundle\EmailBundle\Mailer\Processor as BaseProcessor; use Oro\Bundle\EmailBundle\Decoder\ContentDecoder; use Oro\Bundle\EmailBundle\Entity\EmailAttachment; use Oro\Bundle\EmailBundle\Entity\EmailAttachmentContent; -use Oro\Bundle\EmailBundle\Entity\EmailOrigin; -use Oro\Bundle\EmailBundle\Entity\EmailUser; -use Oro\Bundle\EmailBundle\Event\EmailBodyAdded; use Oro\Bundle\EmailBundle\Form\Model\Email as EmailModel; use Oro\Bundle\EmailBundle\Form\Model\EmailAttachment as EmailAttachmentModel; -use Oro\Bundle\EmailBundle\Tools\EmailAddressHelper; +use Oro\Bundle\EmailBundle\Mailer\Processor as BaseProcessor; +use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; class Processor extends BaseProcessor { @@ -34,7 +29,7 @@ protected function prepareMessage(EmailModel $model, $parentMessageId, $messageD } $addresses = $this->getAddresses($model->getFrom()); $address = $this->emailAddressHelper->extractPureEmailAddress($model->getFrom()); - $message->setDate($messageDate->getTimestamp()); + $message->setDate($messageDate); $message->setFrom($addresses); $message->setReplyTo($addresses); $message->setReturnPath($address); @@ -78,7 +73,7 @@ function ($matches) use ($message, $guesser, $model) { list($encoding, $file) = explode(',', $content); $mime = str_replace('data:', '', $mime); $fileName = sprintf('%s.%s', uniqid(), $guesser->guess($mime)); - $swiftAttachment = \Swift_Image::newInstance( + $swiftAttachment = new \Swift_Image( ContentDecoder::decode($file, $encoding), $fileName, $mime diff --git a/src/Marello/Bundle/CoreBundle/MarelloCoreBundle.php b/src/Marello/Bundle/CoreBundle/MarelloCoreBundle.php index 13a097fc1..ebf5277bb 100644 --- a/src/Marello/Bundle/CoreBundle/MarelloCoreBundle.php +++ b/src/Marello/Bundle/CoreBundle/MarelloCoreBundle.php @@ -13,7 +13,7 @@ class MarelloCoreBundle extends Bundle { public function build(ContainerBuilder $container) { - $container->addCompilerPass(new TwigSandboxConfigurationPass()); + //$container->addCompilerPass(new TwigSandboxConfigurationPass()); $container->addCompilerPass(new OroEmailProcessorOverrideServiceCompilerPass()); $container->addCompilerPass(new AdditionalPlaceholderProviderPass()); parent::build($container); diff --git a/src/Marello/Bundle/CoreBundle/Migration/UpdateExtendRelationTrait.php b/src/Marello/Bundle/CoreBundle/Migration/UpdateExtendRelationTrait.php new file mode 100644 index 000000000..b100217c1 --- /dev/null +++ b/src/Marello/Bundle/CoreBundle/Migration/UpdateExtendRelationTrait.php @@ -0,0 +1,76 @@ +clearCache(); + + $entityConfigModel = $configManager->getConfigEntityModel($entityFrom); + if (!$entityConfigModel) { + return; + } + $data = $entityConfigModel->toArray('extend'); + + $fullRelationFrom = implode( + '|', + [$relationType, $entityFrom, $entityTo, $relationFrom] + ); + $fullRelationTo = implode( + '|', + [$relationType, $entityFrom, $entityTo, $relationTo] + ); + if (array_key_exists($fullRelationFrom, $data['relation'])) { + $data['relation'][$fullRelationTo] = + $data['relation'][$fullRelationFrom]; + unset($data['relation'][$fullRelationFrom]); + + if (isset($data['relation'][$fullRelationTo]['field_id'])) { + /** @var FieldConfigId $fieldId */ + $fieldId = $data['relation'][$fullRelationTo]['field_id']; + $reflectionProperty = new \ReflectionProperty(get_class($fieldId), 'fieldName'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($fieldId, $relationTo); + $data['relation'][$fullRelationTo]['field_id'] = $fieldId; + } + } + + if (array_key_exists($relationFrom, $data['schema']['relation'])) { + $data['schema']['relation'][$relationTo] = + $data['schema']['relation'][$relationFrom]; + unset($data['schema']['relation'][$relationFrom]); + } + if (array_key_exists($relationFrom, $data['schema']['addremove'])) { + $data['schema']['addremove'][$relationTo] = + $data['schema']['addremove'][$relationFrom]; + unset($data['schema']['addremove'][$relationFrom]); + } + + $entityConfigModel->fromArray('extend', $data, []); + $configManager->updateConfigEntityModel($entityFrom, true); + + $configManager->changeFieldName($entityFrom, $relationFrom, $relationTo); + + $configManager->flush(); + $configManager->clearCache(); + } +} diff --git a/src/Marello/Bundle/CoreBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/CoreBundle/Resources/config/jsmodules.yml new file mode 100644 index 000000000..6fbac5cf7 --- /dev/null +++ b/src/Marello/Bundle/CoreBundle/Resources/config/jsmodules.yml @@ -0,0 +1,3 @@ +dynamic-imports: + marellocore: + - marellocore/js/app/elements-helper \ No newline at end of file diff --git a/src/Marello/Bundle/CoreBundle/Resources/config/oro/app.yml b/src/Marello/Bundle/CoreBundle/Resources/config/oro/app.yml index 2d6a460af..da2d90485 100644 --- a/src/Marello/Bundle/CoreBundle/Resources/config/oro/app.yml +++ b/src/Marello/Bundle/CoreBundle/Resources/config/oro/app.yml @@ -6,13 +6,9 @@ fos_js_routing: oro_help: defaults: - link: 'http://www.marello.com/' - server: http://www.marello.com/ + link: 'https://knowledgebase.marello.com/' + server: https://knowledgebase.marello.com/ prefix: Third_Party - vendors: - Marello: - prefix: ~ - alias: Marello oro_theme: active_theme: oro @@ -21,15 +17,6 @@ oro_theme: icon: bundles/marellolayout/images/marello.ico logo: bundles/marellocore/img/marello-logo.png -oro_sidebar: - sidebar_widgets: - sticky_note: - icon: "bundles/marellolayout/images/marello.svg" - iconClass: ~ - settings: - content: | - Welcome to Marello! - Marello Unified Commerce Management provides capabilities to meet and exceed rising expectations in commerce. Learn more at www.marello.com oro_ui: settings: organization_name: diff --git a/src/Marello/Bundle/CoreBundle/Resources/config/requirejs.yml b/src/Marello/Bundle/CoreBundle/Resources/config/requirejs.yml deleted file mode 100644 index 43a4830d5..000000000 --- a/src/Marello/Bundle/CoreBundle/Resources/config/requirejs.yml +++ /dev/null @@ -1,3 +0,0 @@ -config: - paths: - 'marellocore/js/app/elements-helper': 'bundles/marellocore/js/app/elements-helper.js' diff --git a/src/Marello/Bundle/CoreBundle/Resources/config/services.yml b/src/Marello/Bundle/CoreBundle/Resources/config/services.yml index b4b82c12b..dd4569f4e 100644 --- a/src/Marello/Bundle/CoreBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/CoreBundle/Resources/config/services.yml @@ -1,6 +1,3 @@ -parameters: - marello_core.entity_serializer.class: Marello\Bundle\CoreBundle\Serializer\EntitySerializer - services: marello_core.workflow.action.workflow_transit_action: class: Marello\Bundle\CoreBundle\Workflow\Action\WorkflowTransitAction @@ -52,13 +49,14 @@ services: marello_core.form.url_generator: class: Marello\Bundle\CoreBundle\Form\UrlGenerator + public: true arguments: - '@oro_platform.provider.package' - '@oro_platform.url.cache' - '@request_stack' marello_core.entity_serializer: - class: '%marello_core.entity_serializer.class%' + class: 'Marello\Bundle\CoreBundle\Serializer\EntitySerializer' arguments: - '@doctrine' - '@oro_entity_config.config_manager' @@ -67,7 +65,7 @@ services: - '@oro_soap.entity_serializer.query_factory' - '@oro_workflow.manager' calls: - - ["setFieldsFilter", ['@oro_security.serializer.filter_chain']] + - ["setFieldFilter", ['@oro_security.entity_serializer.field_filter']] marello_core.provider.additional_placeholder_provider: class: 'Marello\Bundle\CoreBundle\Provider\AdditionalPlaceholderProvider' diff --git a/src/Marello/Bundle/CoreBundle/Resources/public/js/app/elements-helper.js b/src/Marello/Bundle/CoreBundle/Resources/public/js/app/elements-helper.js index 9c641adc7..8ab513bef 100644 --- a/src/Marello/Bundle/CoreBundle/Resources/public/js/app/elements-helper.js +++ b/src/Marello/Bundle/CoreBundle/Resources/public/js/app/elements-helper.js @@ -4,8 +4,8 @@ define(function(require) { /** * This helper use in the context of component View */ - var $ = require('jquery'); - var _ = require('underscore'); + const $ = require('jquery'); + const _ = require('underscore'); require('jquery.validate'); return { diff --git a/src/Marello/Bundle/CoreBundle/Resources/public/sidebar_widgets/sticky_note/widget.yml b/src/Marello/Bundle/CoreBundle/Resources/public/sidebar_widgets/sticky_note/widget.yml new file mode 100644 index 000000000..4a46bb564 --- /dev/null +++ b/src/Marello/Bundle/CoreBundle/Resources/public/sidebar_widgets/sticky_note/widget.yml @@ -0,0 +1,11 @@ +title: oro.sidebar.sticky_note_widget.title +iconClass: "fa-sticky-note-o" +dialogIcon: "bundles/orosidebar/sidebar_widgets/sticky_note/img/icon-sticky.png" +module: "orosidebar/widget/sticky-note" +placement: "both" +cssClass: 'sidebar-widget-sticky-note' +description: oro.sidebar.sticky_note_widget.description +settings: + content: | + Welcome to Marello! + Marello is a Unified Commerce Management Solution with built-in tools for your Commerce business. Learn more at www.marello.com \ No newline at end of file diff --git a/src/Marello/Bundle/CoreBundle/Resources/views/call_on_me.html.twig b/src/Marello/Bundle/CoreBundle/Resources/views/call_on_me.html.twig index 9589b8cbd..9363c6f02 100644 --- a/src/Marello/Bundle/CoreBundle/Resources/views/call_on_me.html.twig +++ b/src/Marello/Bundle/CoreBundle/Resources/views/call_on_me.html.twig @@ -1,5 +1,5 @@ - {% endblock messages %} -
+
- {% if marello_order_order_address is not defined or resource_granted(marello_order_order_address) %} + {% if marello_order_order_address is not defined or is_granted(marello_order_order_address) %}
{% endif %} -
{{ UI.renderHtmlProperty(label|trans, address.renderAddress(orderAddress)) }} {{ UI.renderProperty('marello.order.address.phone.label'|trans, orderAddress.phone) }}
-
-
\ No newline at end of file + diff --git a/src/Marello/Bundle/OrderBundle/Resources/views/Order/widget/updateAddress.html.twig b/src/Marello/Bundle/OrderBundle/Resources/views/Order/widget/updateAddress.html.twig index 4f43bd9a7..c73aaedff 100644 --- a/src/Marello/Bundle/OrderBundle/Resources/views/Order/widget/updateAddress.html.twig +++ b/src/Marello/Bundle/OrderBundle/Resources/views/Order/widget/updateAddress.html.twig @@ -1,44 +1,25 @@ {% form_theme form with ['OroFormBundle:Form:fields.html.twig', _self] %} -
-
- {% block messages %} -
-
-
- - {% endblock messages %} -
- {% if saved %} - - {% else %} +{% if saved %} + {% set widgetResponse = { + widget: { + trigger: [{ + eventBroker: 'widget', + name: 'formSave', + args: [form.vars.value.id], + }], + } + } %} + + {{ widgetResponse|json_encode|raw }} +{% else %} +
{% if not form.vars.valid and form_errors(form) %} -
-
- {{ form_errors(form) }} +
+
+ {{ form_errors(form) }} +
-
{% endif %} -
@@ -51,5 +32,5 @@
{{ oro_form_js_validation(form) }} - {% endif %} -
+
+{% endif %} diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/OrderJsonApiTest.php b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/OrderJsonApiTest.php index 8c983ebd6..3f4e84c5d 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/OrderJsonApiTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/OrderJsonApiTest.php @@ -5,7 +5,7 @@ use Symfony\Component\HttpFoundation\Response; use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\CoreBundle\Tests\Functional\RestJsonApiTestCase; use Marello\Bundle\OrderBundle\Tests\Functional\DataFixtures\LoadOrderData; use Marello\Bundle\OrderBundle\Tests\Functional\DataFixtures\LoadOrderWorkflowData; diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_existing_customer.yml b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_existing_customer.yml index 47b8b3dcf..66e901df3 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_existing_customer.yml +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_existing_customer.yml @@ -7,7 +7,7 @@ data: totalTax: 0 grandTotal: 101 currency: 'USD' - paymentMethod: null + paymentMethod: 'payment_term_1' paymentReference: null paymentDetails: null shippingAmountInclTax: null @@ -103,7 +103,6 @@ included: attributes: quantity: 10 productName: 'name)>' - quantity: 0 price: 0 originalPriceInclTax: 0 originalPriceExclTax: 0 @@ -127,7 +126,6 @@ included: attributes: quantity: 10 productName: 'name)>' - quantity: 0 price: 0 originalPriceInclTax: 0 originalPriceExclTax: 0 diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_new_customer.yml b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_new_customer.yml index 598ff9c6e..9b2b771ad 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_new_customer.yml +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/requests/order_create_with_new_customer.yml @@ -3,6 +3,7 @@ data: attributes: orderReference: 'NOPENOPE' currency: 'USD' + paymentMethod: 'payment_term_1' shippingMethod: 'manual_shipping_1' shippingMethodType: 'primary' relationships: diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/cget_order_list.yml b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/cget_order_list.yml index da660add9..9fb62c1a1 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/cget_order_list.yml +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/cget_order_list.yml @@ -26,7 +26,6 @@ data: salesChannelName: channel2 purchaseDate: '@marello_order_0->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -88,7 +87,6 @@ data: salesChannelName: channel3 purchaseDate: '@marello_order_1->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -159,7 +157,6 @@ data: salesChannelName: channel2 purchaseDate: '@marello_order_2->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -227,7 +224,6 @@ data: salesChannelName: channel3 purchaseDate: '@marello_order_3->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -298,7 +294,6 @@ data: salesChannelName: channel3 purchaseDate: '@marello_order_4->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -360,7 +355,6 @@ data: salesChannelName: channel3 purchaseDate: '@marello_order_5->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -425,7 +419,6 @@ data: salesChannelName: channel3 purchaseDate: '@marello_order_6->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -496,7 +489,6 @@ data: salesChannelName: channel2 purchaseDate: '@marello_order_7->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -558,7 +550,6 @@ data: salesChannelName: channel2 purchaseDate: '@marello_order_8->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending @@ -623,7 +614,6 @@ data: salesChannelName: channel1 purchaseDate: '@marello_order_9->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_id.yml b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_id.yml index fee393626..2fa1f090e 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_id.yml +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_id.yml @@ -25,7 +25,6 @@ data: salesChannelName: channel3 purchaseDate: '@marello_order_1->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_orderNumber.yml b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_orderNumber.yml index fee393626..2fa1f090e 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_orderNumber.yml +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Api/responses/get_order_by_orderNumber.yml @@ -25,7 +25,6 @@ data: salesChannelName: channel3 purchaseDate: '@marello_order_1->purchaseDate->format("Y-m-d\TH:i:s\Z")' data: null - locale: null workflowItem: currentStep: name: pending diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderControllerTest.php b/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderControllerTest.php index a1deb7fad..2c4bce80d 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderControllerTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderControllerTest.php @@ -3,9 +3,10 @@ namespace Marello\Bundle\OrderBundle\Tests\Functional\Controller; use Marello\Bundle\AddressBundle\Entity\MarelloAddress; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Tests\Functional\DataFixtures\LoadOrderData; +use Marello\Bundle\PaymentBundle\Method\PaymentMethodInterface; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\ProductBundle\Tests\Functional\DataFixtures\LoadProductData; use Marello\Bundle\SalesBundle\Entity\SalesChannel; @@ -181,6 +182,10 @@ public function testUpdateAddress() */ private function getSubmittedData($form, $orderCustomer, $salesChannel, $orderItems) { + $paymentMethodProvider = $this->getContainer()->get('marello_payment.payment_method.composite_provider'); + $paymentMethods = $paymentMethodProvider->getPaymentMethods(); + /** @var PaymentMethodInterface $paymentMethod */ + $paymentMethod = reset($paymentMethods); /** @var ShippingMethodProviderInterface $shippingMethodsProvider */ $shippingMethodsProvider = $this->getContainer()->get('marello_shipping.shipping_method_provider'); $shippingMethods = $shippingMethodsProvider->getShippingMethods(); @@ -199,6 +204,7 @@ private function getSubmittedData($form, $orderCustomer, $salesChannel, $orderIt 'billingAddress' => $this->getAddressFormData($orderCustomer->getPrimaryAddress()), 'shippingAddress' => $this->getAddressFormData($orderCustomer->getPrimaryAddress()), 'calculateShipping' => true, + 'paymentMethod' => $paymentMethod->getIdentifier(), 'shippingMethod' => $shippingMethod->getIdentifier(), 'shippingMethodType' => $shippingMethodType->getIdentifier(), 'estimatedShippingCostAmount' => 5.00 diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderOnDemandWorkflowTest.php b/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderOnDemandWorkflowTest.php index 4300207f7..c2d6b56c2 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderOnDemandWorkflowTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/Controller/OrderOnDemandWorkflowTest.php @@ -3,11 +3,12 @@ namespace Marello\Bundle\OrderBundle\Tests\Functional\Controller; use Marello\Bundle\AddressBundle\Entity\MarelloAddress; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Tests\Functional\DataFixtures\LoadOrderData; use Marello\Bundle\PackingBundle\Entity\PackingSlip; use Marello\Bundle\PackingBundle\Entity\PackingSlipItem; +use Marello\Bundle\PaymentBundle\Method\PaymentMethodInterface; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\ProductBundle\Tests\Functional\DataFixtures\LoadProductData; use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrder; @@ -196,6 +197,10 @@ public function testWorkflow() */ private function getSubmittedData($form, $orderCustomer, $salesChannel, $orderItems) { + $paymentMethodProvider = $this->getContainer()->get('marello_payment.payment_method.composite_provider'); + $paymentMethods = $paymentMethodProvider->getPaymentMethods(); + /** @var PaymentMethodInterface $paymentMethod */ + $paymentMethod = reset($paymentMethods); /** @var ShippingMethodProviderInterface $shippingMethodsProvider */ $shippingMethodsProvider = $this->getContainer()->get('marello_shipping.shipping_method_provider'); $shippingMethods = $shippingMethodsProvider->getShippingMethods(); @@ -214,6 +219,7 @@ private function getSubmittedData($form, $orderCustomer, $salesChannel, $orderIt 'billingAddress' => $this->getAddressFormData($orderCustomer->getPrimaryAddress()), 'shippingAddress' => $this->getAddressFormData($orderCustomer->getPrimaryAddress()), 'calculateShipping' => true, + 'paymentMethod' => $paymentMethod->getIdentifier(), 'shippingMethod' => $shippingMethod->getIdentifier(), 'shippingMethodType' => $shippingMethodType->getIdentifier(), 'estimatedShippingCostAmount' => 5.00 diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/DataFixtures/LoadOrderData.php b/src/Marello/Bundle/OrderBundle/Tests/Functional/DataFixtures/LoadOrderData.php index ec36abfa7..ff483b75c 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/DataFixtures/LoadOrderData.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/DataFixtures/LoadOrderData.php @@ -6,8 +6,9 @@ use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Marello\Bundle\AddressBundle\Entity\MarelloAddress; +use Marello\Bundle\CustomerBundle\Tests\Functional\DataFixtures\LoadCustomerData; use Marello\Bundle\InventoryBundle\Entity\Warehouse; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderItem; use Marello\Bundle\PricingBundle\Tests\Functional\DataFixtures\LoadProductChannelPricingData; diff --git a/src/Marello/Bundle/OrderBundle/Tests/Functional/EventListener/Doctrine/OrderWorkflowStartListenerTest.php b/src/Marello/Bundle/OrderBundle/Tests/Functional/EventListener/Doctrine/OrderWorkflowStartListenerTest.php index f0a986673..be407c0b2 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Functional/EventListener/Doctrine/OrderWorkflowStartListenerTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Functional/EventListener/Doctrine/OrderWorkflowStartListenerTest.php @@ -2,6 +2,7 @@ namespace Marello\Bundle\OrderBundle\Tests\Functional\EventListener\Doctrine; +use Marello\Bundle\PaymentBundle\Method\PaymentMethodInterface; use Symfony\Component\DomCrawler\Form; use Symfony\Component\HttpFoundation\Response; @@ -10,7 +11,7 @@ use Oro\Bundle\TestFrameworkBundle\Test\WebTestCase; use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Marello\Bundle\AddressBundle\Entity\MarelloAddress; @@ -159,6 +160,10 @@ private function createOrder() */ private function getSubmittedData($form, $orderCustomer, $salesChannel, $orderItems) { + $paymentMethodProvider = $this->getContainer()->get('marello_payment.payment_method.composite_provider'); + $paymentMethods = $paymentMethodProvider->getPaymentMethods(); + /** @var PaymentMethodInterface $paymentMethod */ + $paymentMethod = reset($paymentMethods); /** @var ShippingMethodProviderInterface $shippingMethodsProvider */ $shippingMethodsProvider = $this->getContainer()->get('marello_shipping.shipping_method_provider'); $shippingMethods = $shippingMethodsProvider->getShippingMethods(); @@ -177,6 +182,7 @@ private function getSubmittedData($form, $orderCustomer, $salesChannel, $orderIt 'billingAddress' => $this->getAddressFormData($orderCustomer->getPrimaryAddress()), 'shippingAddress' => $this->getAddressFormData($orderCustomer->getPrimaryAddress()), 'calculateShipping' => true, + 'paymentMethod' => $paymentMethod->getIdentifier(), 'shippingMethod' => $shippingMethod->getIdentifier(), 'shippingMethodType' => $shippingMethodType->getIdentifier(), 'estimatedShippingCostAmount' => 5.00 diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Entity/OrderTest.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Entity/OrderTest.php index 79cf00cb4..22227c963 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Unit/Entity/OrderTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Entity/OrderTest.php @@ -10,7 +10,7 @@ use Oro\Bundle\OrganizationBundle\Entity\Organization; use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\OrderBundle\Entity\OrderItem; use Marello\Bundle\SalesBundle\Entity\SalesChannel; @@ -46,7 +46,6 @@ public function testAccessors() ['salesChannelName', 'some string'], ['organization', new Organization()], ['localization', new Localization()], - ['locale', 'some string'], ['createdAt', new \DateTime()], ['updatedAt', new \DateTime()] ]); diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/DiscountSubtotalProviderTest.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/DiscountSubtotalProviderTest.php index a7690cdfb..b114ab7b9 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/DiscountSubtotalProviderTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/DiscountSubtotalProviderTest.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\OrderBundle\Tests\Unit\Provider; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use PHPUnit\Framework\TestCase; diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderDashboardOrderItemsByStatusProviderTest.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderDashboardOrderItemsByStatusProviderTest.php index bd9ad83ce..89c66f008 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderDashboardOrderItemsByStatusProviderTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderDashboardOrderItemsByStatusProviderTest.php @@ -14,8 +14,9 @@ use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper; use Symfony\Bridge\Doctrine\RegistryInterface; +use PHPUnit\Framework\TestCase; -class OrderDashboardOrderItemsByStatusProviderTest extends \PHPUnit\Framework\TestCase +class OrderDashboardOrderItemsByStatusProviderTest extends TestCase { /** @var RegistryInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $registry; diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItem/OrderItemFormChangesProviderTest.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItem/OrderItemFormChangesProviderTest.php index 80d7ffe69..69df438ce 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItem/OrderItemFormChangesProviderTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItem/OrderItemFormChangesProviderTest.php @@ -2,8 +2,9 @@ namespace Marello\Bundle\OrderBundle\Tests\Unit\Provider\OrderItem; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Form\FormInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use PHPUnit\Framework\TestCase; @@ -37,7 +38,7 @@ protected function setUp() /** * @param $data - * @return FormChangesProviderInterface|\PHPUnit_Framework_MockObject_Builder_InvocationMocker + * @return FormChangesProviderInterface|MockObject */ protected function createProviderMock($data) { diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItemsSubtotalProviderTest.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItemsSubtotalProviderTest.php index 1479734c2..2abf26d1e 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItemsSubtotalProviderTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderItemsSubtotalProviderTest.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\PricingBundle\Tests\Unit\Provider; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use PHPUnit\Framework\TestCase; diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderLocalizationProviderTest.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderLocalizationProviderTest.php index 4cfc64321..f033e2768 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderLocalizationProviderTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Provider/OrderLocalizationProviderTest.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\OrderBundle\Tests\Unit\Provider; -use Marello\Bundle\LocaleBundle\Model\LocaleAwareInterface; +use Marello\Bundle\LocaleBundle\Model\LocalizationAwareInterface; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderAwareInterface; use Marello\Bundle\OrderBundle\Provider\OrderLocalizationProvider; @@ -39,7 +39,7 @@ public function testOrderAwareEntity() $order ->expects(static::once()) ->method('getLocalization'); - $entity = $this->createMock([OrderAwareInterface::class, LocaleAwareInterface::class]); + $entity = $this->createMock([OrderAwareInterface::class, LocalizationAwareInterface::class]); $entity ->expects(static::once()) ->method('getOrder') diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Stub/StatusEnumClassStub.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Stub/StatusEnumClassStub.php new file mode 100644 index 000000000..172934723 --- /dev/null +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Stub/StatusEnumClassStub.php @@ -0,0 +1,17 @@ +workflowManager = $this->createMock(WorkflowManager::class); - + $this->extension = new OrderExtension(); /** @var Registry $registry */ $registry = $this ->getMockBuilder(Registry::class) @@ -44,7 +36,6 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->extension = new OrderExtension($this->workflowManager); $this->extension ->setItemsForNotificationProvider($orderItemsForNotificationProvider) ->setRegistry($registry); @@ -94,12 +85,22 @@ public function testGetFunctionsAreRegisteredInExtension() */ public function testIfOrderCanReturnIsTrue() { - /** @var Order $order */ + /** @var Order|\PHPUnit_Framework_MockObject_MockObject $order */ $order = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() ->getMock(); - $orderItem = new OrderItem(); - $orderItem->setStatus(LoadOrderItemStatusData::SHIPPED); + + $orderItemStatusMock = $this->createMock(StatusEnumClassStub::class); + $orderItemStatusMock + ->expects(static::atLeastOnce()) + ->method('getId') + ->willReturn(LoadOrderItemStatusData::SHIPPED); + + $orderItem = $this->createMock(OrderItem::class); + $orderItem + ->expects(static::atLeastOnce()) + ->method('getStatus') + ->willReturn($orderItemStatusMock); $order ->expects($this->once()) ->method('getItems') @@ -113,12 +114,23 @@ public function testIfOrderCanReturnIsTrue() */ public function testIfOrderCanReturnIsFalse() { - /** @var Order $order */ + /** @var Order|\PHPUnit_Framework_MockObject_MockObject $order */ $order = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() ->getMock(); - $orderItem = new OrderItem(); - $orderItem->setStatus(LoadOrderItemStatusData::PROCESSING); + + $orderItemStatusMock = $this->createMock(StatusEnumClassStub::class); + $orderItemStatusMock + ->expects(static::atLeastOnce()) + ->method('getId') + ->willReturn(LoadOrderItemStatusData::PROCESSING); + + $orderItem = $this->createMock(OrderItem::class); + $orderItem + ->expects(static::atLeastOnce()) + ->method('getStatus') + ->willReturn($orderItemStatusMock); + $order ->expects($this->once()) ->method('getItems') diff --git a/src/Marello/Bundle/OrderBundle/Tests/Unit/Validator/AvailableInventoryValidatorTest.php b/src/Marello/Bundle/OrderBundle/Tests/Unit/Validator/AvailableInventoryValidatorTest.php index 8d9ba8817..852e967a2 100644 --- a/src/Marello/Bundle/OrderBundle/Tests/Unit/Validator/AvailableInventoryValidatorTest.php +++ b/src/Marello/Bundle/OrderBundle/Tests/Unit/Validator/AvailableInventoryValidatorTest.php @@ -333,9 +333,10 @@ protected function getValidator() { $validator = new AvailableInventoryValidator( $this->doctrineHelper, - $this->inventoryProvider + $this->inventoryProvider, + $this->eventDispatcher ); - $validator->setEventDispatcher($this->eventDispatcher); + $validator->initialize($this->context); return $validator; diff --git a/src/Marello/Bundle/OrderBundle/Twig/OrderExtension.php b/src/Marello/Bundle/OrderBundle/Twig/OrderExtension.php index 1a58ac405..c387c1177 100644 --- a/src/Marello/Bundle/OrderBundle/Twig/OrderExtension.php +++ b/src/Marello/Bundle/OrderBundle/Twig/OrderExtension.php @@ -3,16 +3,15 @@ namespace Marello\Bundle\OrderBundle\Twig; use Doctrine\Bundle\DoctrineBundle\Registry; - -use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; -use Oro\Bundle\WorkflowBundle\Model\WorkflowManager; - use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderItem; use Marello\Bundle\OrderBundle\Migrations\Data\ORM\LoadOrderItemStatusData; use Marello\Bundle\OrderBundle\Provider\OrderItem\ShippingPreparedOrderItemsForNotificationProvider; +use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class OrderExtension extends \Twig_Extension +class OrderExtension extends AbstractExtension { const NAME = 'marello_order'; @@ -22,19 +21,6 @@ class OrderExtension extends \Twig_Extension /** @var ShippingPreparedOrderItemsForNotificationProvider $orderItemsForNotificationProvider*/ private $orderItemsForNotificationProvider; - /** @var WorkflowManager $workflowManager */ - protected $workflowManager; - - /** - * ProductExtension constructor. - * - * @param WorkflowManager $workflowManager - */ - public function __construct(WorkflowManager $workflowManager) - { - $this->workflowManager = $workflowManager; - } - /** * Returns the name of the extension. * @@ -53,19 +39,19 @@ public function getName() public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_order_can_return', [$this, 'canReturn'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_order_item_shipped', [$this, 'isShippedOrderItem'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_get_order_item_status', [$this, 'findStatusByName'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_get_order_items_for_notification', [$this->orderItemsForNotificationProvider, 'getItems'] ) @@ -80,7 +66,7 @@ public function getFunctions() public function canReturn(Order $order) { foreach ($order->getItems() as $orderItem) { - if (!in_array($orderItem->getStatus(), $this->getOrderItemStatuses())) { + if (!in_array($orderItem->getStatus()->getId(), $this->getOrderItemStatuses(), true)) { return false; } } @@ -94,7 +80,7 @@ public function canReturn(Order $order) */ public function isShippedOrderItem(OrderItem $orderItem) { - if (in_array($orderItem->getStatus(), $this->getOrderItemStatuses())) { + if (in_array($orderItem->getStatus()->getId(), $this->getOrderItemStatuses(), true)) { return true; } diff --git a/src/Marello/Bundle/OrderBundle/Validator/AvailableInventoryValidator.php b/src/Marello/Bundle/OrderBundle/Validator/AvailableInventoryValidator.php index ea3707f08..538152e1e 100644 --- a/src/Marello/Bundle/OrderBundle/Validator/AvailableInventoryValidator.php +++ b/src/Marello/Bundle/OrderBundle/Validator/AvailableInventoryValidator.php @@ -55,10 +55,12 @@ class AvailableInventoryValidator extends ConstraintValidator */ public function __construct( DoctrineHelper $doctrineHelper, - AvailableInventoryProvider $availableInventoryProvider + AvailableInventoryProvider $availableInventoryProvider, + EventDispatcherInterface $eventDispatcher ) { $this->doctrineHelper = $doctrineHelper; $this->availableInventoryProvider = $availableInventoryProvider; + $this->eventDispatcher = $eventDispatcher; } /** @@ -339,17 +341,4 @@ private function getErrorPathFromConfig(Constraint $constraint, $fields) { return null !== $constraint->errorPath ? $constraint->errorPath : $fields[0]; } - - /** - * Added for keeping BC - * @deprecated will be removed in 3.0 - * @param EventDispatcherInterface $eventDispatcher - * @return $this - */ - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - - return $this; - } } diff --git a/src/Marello/Bundle/OrderBundle/Workflow/OrderShipAction.php b/src/Marello/Bundle/OrderBundle/Workflow/OrderShipAction.php index 91241948e..a18e65e74 100644 --- a/src/Marello/Bundle/OrderBundle/Workflow/OrderShipAction.php +++ b/src/Marello/Bundle/OrderBundle/Workflow/OrderShipAction.php @@ -3,19 +3,17 @@ namespace Marello\Bundle\OrderBundle\Workflow; use Doctrine\Bundle\DoctrineBundle\Registry; - +use Marello\Bundle\InventoryBundle\Entity\InventoryBatch; +use Marello\Bundle\InventoryBundle\Entity\Warehouse; +use Marello\Bundle\InventoryBundle\Event\InventoryUpdateEvent; use Marello\Bundle\InventoryBundle\Model\InventoryUpdateContextFactory; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -use Oro\Bundle\WorkflowBundle\Entity\WorkflowItem; -use Oro\Component\ConfigExpression\ContextAccessor; - +use Marello\Bundle\InventoryBundle\Provider\OrderWarehousesProviderInterface; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderItem; -use Marello\Bundle\InventoryBundle\Event\InventoryUpdateEvent; -use Marello\Bundle\InventoryBundle\Model\InventoryUpdateContext; - -use Marello\Bundle\InventoryBundle\Provider\OrderWarehousesProviderInterface; +use Marello\Bundle\PackingBundle\Entity\PackingSlipItem; +use Oro\Bundle\WorkflowBundle\Entity\WorkflowItem; +use Oro\Component\ConfigExpression\ContextAccessor; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; class OrderShipAction extends OrderTransitionAction { @@ -71,6 +69,7 @@ protected function executeAction($context) * @param $inventoryUpdateQty * @param $allocatedInventoryQty * @param Order $entity + * @param Warehouse $warehouse */ protected function handleInventoryUpdate($item, $inventoryUpdateQty, $allocatedInventoryQty, $entity, $warehouse) { @@ -82,7 +81,33 @@ protected function handleInventoryUpdate($item, $inventoryUpdateQty, $allocatedI 'order_workflow.shipped', $entity ); - + $packingSlipItem = $this->doctrine + ->getManagerForClass(PackingSlipItem::class) + ->getRepository(PackingSlipItem::class) + ->findOneBy(['orderItem' => $item]); + if ($packingSlipItem) { + if (!empty($packingSlipItem->getInventoryBatches())) { + $contextBranches = []; + foreach ($packingSlipItem->getInventoryBatches() as $batchNumber => $qty) { + /** @var InventoryBatch[] $inventoryBatches */ + $inventoryBatches = $this->doctrine + ->getManagerForClass(InventoryBatch::class) + ->getRepository(InventoryBatch::class) + ->findBy(['batchNumber' => $batchNumber]); + $inventoryBatch = null; + foreach ($inventoryBatches as $batch) { + $inventoryLevel = $batch->getInventoryLevel(); + if ($inventoryLevel && $inventoryLevel->getWarehouse() === $warehouse) { + $inventoryBatch = $batch; + } + } + if ($inventoryBatch) { + $contextBranches[] = ['batch' => $inventoryBatch, 'qty' => -$qty]; + } + } + $context->setInventoryBatches($contextBranches); + } + } $context->setValue('warehouse', $warehouse); $this->eventDispatcher->dispatch( diff --git a/src/Marello/Bundle/PackingBundle/Controller/PackingSlipController.php b/src/Marello/Bundle/PackingBundle/Controller/PackingSlipController.php index 061a1f106..2e25f10b1 100644 --- a/src/Marello/Bundle/PackingBundle/Controller/PackingSlipController.php +++ b/src/Marello/Bundle/PackingBundle/Controller/PackingSlipController.php @@ -2,21 +2,20 @@ namespace Marello\Bundle\PackingBundle\Controller; -use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\PackingBundle\Entity\PackingSlip; -use Marello\Bundle\ReturnBundle\Entity\ReturnEntity; -use Marello\Bundle\ReturnBundle\Form\Type\ReturnUpdateType; - -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\HttpFoundation\Request; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Routing\Annotation\Route; -class PackingSlipController extends Controller +class PackingSlipController extends AbstractController { /** - * @Config\Route("/", name="marello_packing_packingslip_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_packing_packingslip_index" + * ) + * @Template("MarelloPackingBundle:PackingSlip:index.html.twig") * @AclAncestor("marello_packing_slip_view") */ public function indexAction() @@ -25,8 +24,12 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_packing_packingslip_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_packing_packingslip_view" + * ) + * @Template("MarelloPackingBundle:PackingSlip:view.html.twig") * @AclAncestor("marello_packing_slip_view") * * @param PackingSlip $packingSlip diff --git a/src/Marello/Bundle/PackingBundle/Entity/PackingSlip.php b/src/Marello/Bundle/PackingBundle/Entity/PackingSlip.php index 2430d4c1e..927ba754d 100644 --- a/src/Marello/Bundle/PackingBundle/Entity/PackingSlip.php +++ b/src/Marello/Bundle/PackingBundle/Entity/PackingSlip.php @@ -2,22 +2,26 @@ namespace Marello\Bundle\PackingBundle\Entity; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Marello\Bundle\AddressBundle\Entity\MarelloAddress; -use Marello\Bundle\CoreBundle\DerivedProperty\DerivedPropertyAwareInterface; -use Marello\Bundle\CoreBundle\Model\EntityCreatedUpdatedAtTrait; -use Marello\Bundle\InventoryBundle\Entity\Warehouse; -use Marello\Bundle\OrderBundle\Entity\Customer; -use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\PackingBundle\Model\ExtendPackingSlip; -use Marello\Bundle\SalesBundle\Entity\SalesChannel; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; + +use Symfony\Component\Validator\Constraints as Assert; + use Oro\Bundle\AddressBundle\Entity\AbstractAddress; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; use Oro\Bundle\OrganizationBundle\Entity\OrganizationAwareInterface; use Oro\Bundle\OrganizationBundle\Entity\Ownership\AuditableOrganizationAwareTrait; -use Symfony\Component\Validator\Constraints as Assert; + +use Marello\Bundle\OrderBundle\Entity\Order; +use Marello\Bundle\CustomerBundle\Entity\Customer; +use Marello\Bundle\SalesBundle\Entity\SalesChannel; +use Marello\Bundle\InventoryBundle\Entity\Warehouse; +use Marello\Bundle\AddressBundle\Entity\MarelloAddress; +use Marello\Bundle\PackingBundle\Model\ExtendPackingSlip; +use Marello\Bundle\CoreBundle\Model\EntityCreatedUpdatedAtTrait; +use Marello\Bundle\SalesBundle\Model\SalesChannelAwareInterface; +use Marello\Bundle\CoreBundle\DerivedProperty\DerivedPropertyAwareInterface; /** * @ORM\Entity() @@ -43,7 +47,10 @@ * @ORM\Table(name="marello_packing_packing_slip") * @ORM\HasLifecycleCallbacks() */ -class PackingSlip extends ExtendPackingSlip implements DerivedPropertyAwareInterface, OrganizationAwareInterface +class PackingSlip extends ExtendPackingSlip implements + DerivedPropertyAwareInterface, + OrganizationAwareInterface, + SalesChannelAwareInterface { use EntityCreatedUpdatedAtTrait; use AuditableOrganizationAwareTrait; @@ -118,7 +125,7 @@ class PackingSlip extends ExtendPackingSlip implements DerivedPropertyAwareInter protected $shippingAddress; /** - * @ORM\ManyToOne(targetEntity="Marello\Bundle\OrderBundle\Entity\Customer", cascade={"persist"}) + * @ORM\ManyToOne(targetEntity="Marello\Bundle\CustomerBundle\Entity\Customer", cascade={"persist"}) * @ORM\JoinColumn(name="customer_id", referencedColumnName="id", nullable=false) * @Oro\ConfigField( * defaultValues={ diff --git a/src/Marello/Bundle/PackingBundle/Entity/PackingSlipItem.php b/src/Marello/Bundle/PackingBundle/Entity/PackingSlipItem.php index b6f1350c2..53127e70c 100644 --- a/src/Marello/Bundle/PackingBundle/Entity/PackingSlipItem.php +++ b/src/Marello/Bundle/PackingBundle/Entity/PackingSlipItem.php @@ -3,16 +3,15 @@ namespace Marello\Bundle\PackingBundle\Entity; use Doctrine\ORM\Mapping as ORM; - +use JMS\Serializer\Annotation as JMS; +use Marello\Bundle\CoreBundle\Model\EntityCreatedUpdatedAtTrait; +use Marello\Bundle\OrderBundle\Entity\OrderItem; +use Marello\Bundle\PackingBundle\Model\ExtendPackingSlipItem; +use Marello\Bundle\ProductBundle\Entity\Product; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; use Oro\Bundle\OrganizationBundle\Entity\OrganizationAwareInterface; use Oro\Bundle\OrganizationBundle\Entity\Ownership\AuditableOrganizationAwareTrait; -use Marello\Bundle\OrderBundle\Entity\OrderItem; -use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\PackingBundle\Model\ExtendPackingSlipItem; -use Marello\Bundle\CoreBundle\Model\EntityCreatedUpdatedAtTrait; - /** * @ORM\Entity() * @Oro\Config( @@ -172,6 +171,20 @@ class PackingSlipItem extends ExtendPackingSlipItem implements OrganizationAware */ protected $status; + /** + * @ORM\Column(name="inventory_batches", type="json_array", nullable=true) + * @Oro\ConfigField( + * defaultValues={ + * "dataaudit"={ + * "auditable"=true + * } + * } + * ) + * + * @var array + */ + protected $inventoryBatches; + /** * @ORM\PrePersist */ @@ -370,4 +383,23 @@ public function setStatus($status) return $this; } + + /** + * @return array + */ + public function getInventoryBatches() + { + return $this->inventoryBatches; + } + + /** + * @param array $batches + * @return $this + */ + public function setInventoryBatches(array $batches) + { + $this->inventoryBatches = $batches; + + return $this; + } } diff --git a/src/Marello/Bundle/PackingBundle/EventListener/Datagrid/PackingslipItemsBatchNumbersColumnListener.php b/src/Marello/Bundle/PackingBundle/EventListener/Datagrid/PackingslipItemsBatchNumbersColumnListener.php new file mode 100644 index 000000000..3183e47d8 --- /dev/null +++ b/src/Marello/Bundle/PackingBundle/EventListener/Datagrid/PackingslipItemsBatchNumbersColumnListener.php @@ -0,0 +1,30 @@ +getDatagrid(); + $records = $datagrid->getDatasource()->getResults(); + + $hasInventoryBatches = false; + foreach ($records as $k => $record) { + $value = $record->getValue('inventoryBatches'); + if (!empty($value)) { + $hasInventoryBatches = true; + break; + } + } + if ($hasInventoryBatches === false) { + $config = $datagrid->getConfig(); + $config->offsetSetByPath('[columns][inventoryBatches][renderable]', false); + } + } +} diff --git a/src/Marello/Bundle/PackingBundle/Mapper/OrderToPackingSlipMapper.php b/src/Marello/Bundle/PackingBundle/Mapper/OrderToPackingSlipMapper.php index ca7010929..0d234a7a3 100644 --- a/src/Marello/Bundle/PackingBundle/Mapper/OrderToPackingSlipMapper.php +++ b/src/Marello/Bundle/PackingBundle/Mapper/OrderToPackingSlipMapper.php @@ -4,17 +4,17 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; - -use Symfony\Component\PropertyAccess\PropertyAccessorInterface; - -use Oro\Bundle\EntityBundle\Provider\EntityFieldProvider; - +use Marello\Bundle\InventoryBundle\Entity\InventoryBatch; +use Marello\Bundle\InventoryBundle\Entity\InventoryItem; +use Marello\Bundle\InventoryBundle\Entity\Warehouse; +use Marello\Bundle\InventoryBundle\Provider\OrderWarehousesProviderInterface; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderItem; use Marello\Bundle\PackingBundle\Entity\PackingSlip; use Marello\Bundle\PackingBundle\Entity\PackingSlipItem; use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\InventoryBundle\Provider\OrderWarehousesProviderInterface; +use Oro\Bundle\EntityBundle\Provider\EntityFieldProvider; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; class OrderToPackingSlipMapper extends AbstractPackingSlipMapper { @@ -48,8 +48,9 @@ public function map($sourceEntity) $packingSlip = new PackingSlip(); $data = $this->getData($sourceEntity, PackingSlip::class); $data['order'] = $sourceEntity; - $data['warehouse'] = $result->getWarehouse(); - $data['items'] = $this->getItems($result->getOrderItems()); + $warehouse = $result->getWarehouse(); + $data['warehouse'] = $warehouse; + $data['items'] = $this->getItems($result->getOrderItems(), $warehouse); $this->assignData($packingSlip, $data); $packingSlips[] = $packingSlip; @@ -59,15 +60,16 @@ public function map($sourceEntity) /** * @param Collection $items + * @param Warehouse $warehouse * @return ArrayCollection */ - protected function getItems(Collection $items) + protected function getItems(Collection $items, Warehouse $warehouse) { $orderItems = $items->toArray(); $packingSlipItems = []; /** @var OrderItem $item */ foreach ($orderItems as $item) { - $packingSlipItems[] = $this->mapItem($item); + $packingSlipItems[] = $this->mapItem($item, $warehouse); } return new ArrayCollection($packingSlipItems); @@ -75,17 +77,49 @@ protected function getItems(Collection $items) /** * @param OrderItem $orderItem + * @param Warehouse $warehouse * @return PackingSlipItem */ - protected function mapItem(OrderItem $orderItem) + protected function mapItem(OrderItem $orderItem, Warehouse $warehouse) { $packingSlipItem = new PackingSlipItem(); - $packingSlipData = $this->getData($orderItem, PackingSlipItem::class); + $packingSlipItemData = $this->getData($orderItem, PackingSlipItem::class); /** @var Product $product */ $product = $orderItem->getProduct(); - $packingSlipData['weight'] = $product->getWeight(); - $packingSlipData['orderItem'] = $orderItem; - $this->assignData($packingSlipItem, $packingSlipData); + /** @var InventoryItem $inventoryItem */ + $inventoryItem = $product->getInventoryItems()->first(); + if ($inventoryItem) { + if ($inventoryLevel = $inventoryItem->getInventoryLevel($warehouse)) { + $inventoryBatches = $inventoryLevel->getInventoryBatches()->toArray(); + if (count($inventoryBatches) > 0) { + usort($inventoryBatches, function (InventoryBatch $a, InventoryBatch $b) { + if ($a->getDeliveryDate() < $b->getDeliveryDate()) { + return -1; + } elseif ($a->getDeliveryDate() > $b->getDeliveryDate()) { + return 1; + } else { + return 0; + } + }); + $data = []; + $quantity = $orderItem->getQuantity(); + /** @var InventoryBatch[] $inventoryBatches */ + foreach ($inventoryBatches as $inventoryBatch) { + if ($inventoryBatch->getQuantity() >= $quantity) { + $data[$inventoryBatch->getBatchNumber()] = $quantity; + break; + } elseif ($batchQty = $inventoryBatch->getQuantity() > 0) { + $data[$inventoryBatch->getBatchNumber()] = $batchQty; + $quantity = $quantity - $batchQty; + } + } + $packingSlipItemData['inventoryBatches'] = $data; + } + } + } + $packingSlipItemData['weight'] = $product->getWeight(); + $packingSlipItemData['orderItem'] = $orderItem; + $this->assignData($packingSlipItem, $packingSlipItemData); return $packingSlipItem; } diff --git a/src/Marello/Bundle/PackingBundle/Migrations/Schema/MarelloPackingBundleInstaller.php b/src/Marello/Bundle/PackingBundle/Migrations/Schema/MarelloPackingBundleInstaller.php index 467aece39..15ecb67ee 100644 --- a/src/Marello/Bundle/PackingBundle/Migrations/Schema/MarelloPackingBundleInstaller.php +++ b/src/Marello/Bundle/PackingBundle/Migrations/Schema/MarelloPackingBundleInstaller.php @@ -38,7 +38,7 @@ class MarelloPackingBundleInstaller implements */ public function getMigrationVersion() { - return 'v1_2'; + return 'v1_3'; } /** @@ -96,6 +96,7 @@ protected function createMarelloPackingSlipItemTable(Schema $schema) $table->addColumn('order_item_id', 'integer', []); $table->addColumn('weight', 'float', ['notnull' => true]); $table->addColumn('quantity', 'float', ['notnull' => true]); + $table->addColumn('inventory_batches', 'json_array', ['notnull' => false, 'comment' => '(DC2Type:json_array)']); $table->addColumn('comment', 'text', ['notnull' => false]); $table->addColumn('created_at', 'datetime'); $table->addColumn('updated_at', 'datetime', ['notnull' => false]); @@ -129,7 +130,7 @@ protected function addMarelloPackingSlipForeignKeys(Schema $schema) ['onDelete' => 'SET NULL', 'onUpdate' => null] ); $table->addForeignKeyConstraint( - $schema->getTable('marello_order_customer'), + $schema->getTable('marello_customer_customer'), ['customer_id'], ['id'], ['onDelete' => null, 'onUpdate' => null] diff --git a/src/Marello/Bundle/PackingBundle/Migrations/Schema/v1_3/MarelloPackingBundle.php b/src/Marello/Bundle/PackingBundle/Migrations/Schema/v1_3/MarelloPackingBundle.php new file mode 100644 index 000000000..9c819e770 --- /dev/null +++ b/src/Marello/Bundle/PackingBundle/Migrations/Schema/v1_3/MarelloPackingBundle.php @@ -0,0 +1,56 @@ +updateMarelloPackingSlipTable($schema); + $this->updateMarelloPackingSlipItemTable($schema); + } + + /** + * @param Schema $schema + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function updateMarelloPackingSlipTable(Schema $schema) + { + $table = $schema->getTable('marello_packing_packing_slip'); + if ($table->hasForeignKey('FK_B0E654D9395C3F3')) { + $table->removeForeignKey('FK_B0E654D9395C3F3'); + } + $table->addForeignKeyConstraint( + $schema->getTable('marello_customer_customer'), + ['customer_id'], + ['id'], + ['onDelete' => null, 'onUpdate' => null] + ); + } + + /** + * @param Schema $schema + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function updateMarelloPackingSlipItemTable(Schema $schema) + { + $table = $schema->getTable('marello_packing_pack_slip_item'); + $table->addColumn('inventory_batches', 'json_array', ['notnull' => false, 'comment' => '(DC2Type:json_array)']); + } + + /** + * {@inheritdoc} + */ + public function getOrder() + { + return 20; + } +} diff --git a/src/Marello/Bundle/PackingBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/PackingBundle/Resources/config/oro/datagrids.yml index 3ae3ba9ea..2d31ba27f 100644 --- a/src/Marello/Bundle/PackingBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/PackingBundle/Resources/config/oro/datagrids.yml @@ -48,7 +48,7 @@ datagrids: salesChannel: data_name: sc.name warehouse: - data_name: wh.name + data_name: wh.label billingAddress: data_name: ps.billingAddress shippingAddress: @@ -58,7 +58,7 @@ datagrids: updatedAt: data_name: ps.updatedAt default: - packingSlipNumber: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC" + packingSlipNumber: "DESC" filters: columns: packingSlipNumber: @@ -72,7 +72,7 @@ datagrids: data_name: sc.name warehouse: type: string - data_name: wh.name + data_name: wh.label billingAddress: type: string data_name: ps.billingAddress @@ -140,6 +140,11 @@ datagrids: label: marello.order.orderitem.status.label frontend_type: string data_name: status + inventoryBatches: + label: marello.packing.packingslipitem.inventory_batches.label + type: twig + frontend_type: html + template: MarelloPackingBundle:PackingSlip/Datagrid:inventoryBatches.html.twig createdAt: label: oro.ui.created_at frontend_type: datetime @@ -163,7 +168,7 @@ datagrids: updatedAt: data_name: ps.updatedAt default: - productSku: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + productSku: "ASC" totals: grand_total: columns: diff --git a/src/Marello/Bundle/PackingBundle/Resources/config/services.yml b/src/Marello/Bundle/PackingBundle/Resources/config/services.yml index d2e43439c..7b768071f 100644 --- a/src/Marello/Bundle/PackingBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/PackingBundle/Resources/config/services.yml @@ -22,6 +22,11 @@ services: tags: - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-packingslips, method: onBuildBefore } + marello_packing.listener.datagrid.packingslipitems_batchnumbers_column_listener: + class: 'Marello\Bundle\PackingBundle\EventListener\Datagrid\PackingslipItemsBatchNumbersColumnListener' + tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.after.marello-packingslip-items, method: onBuildAfter } + marello_packing.listener.doctrine.packing_slip_item_status: class: 'Marello\Bundle\PackingBundle\EventListener\Doctrine\PackingSlipItemStatusListener' arguments: diff --git a/src/Marello/Bundle/PackingBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/PackingBundle/Resources/translations/messages.en.yml index 9b79998b7..264fcb119 100644 --- a/src/Marello/Bundle/PackingBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/PackingBundle/Resources/translations/messages.en.yml @@ -37,6 +37,7 @@ marello: comment.label: Comment status.label: Status organization.label: Organization + inventory_batches.label: Inventory Batches datagrid: columns: diff --git a/src/Marello/Bundle/PackingBundle/Resources/views/PackingSlip/Datagrid/inventoryBatches.html.twig b/src/Marello/Bundle/PackingBundle/Resources/views/PackingSlip/Datagrid/inventoryBatches.html.twig new file mode 100644 index 000000000..525c27729 --- /dev/null +++ b/src/Marello/Bundle/PackingBundle/Resources/views/PackingSlip/Datagrid/inventoryBatches.html.twig @@ -0,0 +1,3 @@ +{% for batchNumber, quantity in record.getValue('inventoryBatches') %} + {{ batchNumber ~ ': ' ~ quantity }}
+{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/PackingBundle/Tests/Unit/Entity/PackingSlipTest.php b/src/Marello/Bundle/PackingBundle/Tests/Unit/Entity/PackingSlipTest.php index 0c59d719f..2cd260179 100644 --- a/src/Marello/Bundle/PackingBundle/Tests/Unit/Entity/PackingSlipTest.php +++ b/src/Marello/Bundle/PackingBundle/Tests/Unit/Entity/PackingSlipTest.php @@ -8,7 +8,7 @@ use Oro\Bundle\OrganizationBundle\Entity\Organization; use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Marello\Bundle\InventoryBundle\Entity\Warehouse; use Marello\Bundle\PackingBundle\Entity\PackingSlip; diff --git a/src/Marello/Bundle/PackingBundle/Tests/Unit/Mapper/OrderToPackingSlipMapperTest.php b/src/Marello/Bundle/PackingBundle/Tests/Unit/Mapper/OrderToPackingSlipMapperTest.php index 7095940ac..8e03942fb 100644 --- a/src/Marello/Bundle/PackingBundle/Tests/Unit/Mapper/OrderToPackingSlipMapperTest.php +++ b/src/Marello/Bundle/PackingBundle/Tests/Unit/Mapper/OrderToPackingSlipMapperTest.php @@ -4,6 +4,9 @@ use Doctrine\Common\Collections\ArrayCollection; +use Marello\Bundle\InventoryBundle\Entity\InventoryBatch; +use Marello\Bundle\InventoryBundle\Entity\InventoryItem; +use Marello\Bundle\InventoryBundle\Entity\InventoryLevel; use Symfony\Component\PropertyAccess\PropertyAccess; use PHPUnit\Framework\TestCase; @@ -13,7 +16,7 @@ use Oro\Bundle\EntityBundle\Provider\EntityFieldProvider; use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\OrderBundle\Entity\OrderItem; use Marello\Bundle\SalesBundle\Entity\SalesChannel; @@ -89,11 +92,17 @@ public function testMap() $salesChannel = new SalesChannel(); $customer = new Customer(); $organization = new Organization(); - + $product1 = $this->getEntity(Product::class, ['id' => 1, 'weight' => 2]); $product2 = $this->getEntity(Product::class, ['id' => 2, 'weight' => 3]); $product3 = $this->getEntity(Product::class, ['id' => 3, 'weight' => 5]); + $inventoryLevel1 = $this->getEntity(InventoryLevel::class, ['id' => 1, 'warehouse' => $warehouse]); + $inventoryBatch1 = $this->getEntity(InventoryBatch::class, ['id' => 1, 'batchNumber' => '000001', 'quantity' => 5]); + $inventoryItem1 = new InventoryItem($product1); + $inventoryLevel1->addInventoryBatch($inventoryBatch1); + $inventoryItem1->addInventoryLevel($inventoryLevel1); + $orderItem1 = $this->getEntity(OrderItem::class, ['id' => 1, 'product' => $product1, 'quantity' => 5]); $orderItem2 = $this->getEntity(OrderItem::class, ['id' => 2, 'product' => $product2, 'quantity' => 3]); $orderItem3 = $this->getEntity(OrderItem::class, ['id' => 3, 'product' => $product3, 'quantity' => 1]); @@ -111,6 +120,7 @@ public function testMap() $this->getEntity(PackingSlipItem::class, [ 'orderItem' => $orderItem1, 'product' => $product1, + 'inventoryBatches' => ['000001' => 5], 'quantity' => $orderItem1->getQuantity(), 'weight' => $product1->getWeight() ]), diff --git a/src/Marello/Bundle/PaymentBundle/Action/Handler/PaymentMethodsConfigsRuleToggleStatusActionHandler.php b/src/Marello/Bundle/PaymentBundle/Action/Handler/PaymentMethodsConfigsRuleToggleStatusActionHandler.php new file mode 100644 index 000000000..80b89624e --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Action/Handler/PaymentMethodsConfigsRuleToggleStatusActionHandler.php @@ -0,0 +1,45 @@ +entityManager = $entityManager; + $this->value = (boolean)$value; + } + + /** + * @param PaymentMethodsConfigsRule $configsRule + * @return boolean + */ + public function handleAction(PaymentMethodsConfigsRule $configsRule) + { + if ($configsRule->getRule()->isSystem()) { + return false; + } + $configsRule->getRule()->setEnabled($this->value); + $this->entityManager->persist($configsRule); + $this->entityManager->flush(); + + return true; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierChecker.php b/src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierChecker.php new file mode 100755 index 000000000..c0b4a86d2 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierChecker.php @@ -0,0 +1,31 @@ +paymentMethodProvider = $paymentMethodProvider; + } + + /** + * {@inheritDoc} + */ + public function isEnabled($identifier) + { + return $this->paymentMethodProvider->getPaymentMethod($identifier) !== null ? + $this->paymentMethodProvider->getPaymentMethod($identifier)->isEnabled() : + false; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierCheckerInterface.php b/src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierCheckerInterface.php new file mode 100755 index 000000000..203f577ba --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Checker/PaymentMethodEnabledByIdentifierCheckerInterface.php @@ -0,0 +1,13 @@ +methodEnabledChecker = $methodEnabledChecker; + } + + /** + * {@inheritdoc} + */ + public function canBeEnabled(PaymentMethodsConfigsRule $rule) + { + foreach ($rule->getMethodConfigs() as $config) { + if ($this->methodEnabledChecker->isEnabled($config->getMethod())) { + return true; + } + } + + return false; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Checker/PaymentRuleEnabledCheckerInterface.php b/src/Marello/Bundle/PaymentBundle/Checker/PaymentRuleEnabledCheckerInterface.php new file mode 100755 index 000000000..dda0f43da --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Checker/PaymentRuleEnabledCheckerInterface.php @@ -0,0 +1,15 @@ +propertyPath = $option; + + if (!$this->propertyPath) { + throw new \InvalidArgumentException('Missing "method_identifier" option'); + } + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function isConditionAllowed($context) + { + $paymentMethodIdentifier = $this->resolveValue($context, $this->propertyPath, false); + $methodConfigRules = $this->getRulesByMethod($paymentMethodIdentifier); + + return count($methodConfigRules) !== 0; + } + + /** + * @param $paymentMethodIdentifier + * + * @return PaymentMethodsConfigsRule[] + */ + abstract protected function getRulesByMethod($paymentMethodIdentifier); + + /** + * {@inheritDoc} + */ + public function toArray() + { + return $this->convertToArray([$this->propertyPath]); + } + + /** + * {@inheritDoc} + */ + public function compile($factoryAccessor) + { + return $this->convertToPhpCode([$this->propertyPath], $factoryAccessor); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Condition/HasApplicablePaymentMethods.php b/src/Marello/Bundle/PaymentBundle/Condition/HasApplicablePaymentMethods.php new file mode 100644 index 000000000..52f60ac0e --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Condition/HasApplicablePaymentMethods.php @@ -0,0 +1,103 @@ +paymentMethodProvider = $paymentMethodProvider; + $this->paymentMethodsViewsProvider = $paymentMethodsViewsProvider; + } + + /** + * {@inheritdoc} + */ + public function initialize(array $options) + { + if (array_key_exists('paymentContext', $options)) { + $this->paymentContext = $options['paymentContext']; + } elseif (array_key_exists(0, $options)) { + $this->paymentContext = $options[0]; + } + + if (!$this->paymentContext) { + throw new InvalidArgumentException('Missing "paymentContext" option'); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::NAME; + } + + /** + * {@inheritdoc} + */ + protected function isConditionAllowed($context) + { + /** @var PaymentContextInterface $paymentContext */ + $paymentContext = $this->resolveValue($context, $this->paymentContext, false); + + $methodsData = []; + if (null !== $paymentContext) { + $methodsData = $this->paymentMethodsViewsProvider->getApplicableMethodsViews($paymentContext); + } + + return count($methodsData) !== 0; + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + return $this->convertToArray([$this->paymentContext]); + } + + /** + * {@inheritdoc} + */ + public function compile($factoryAccessor) + { + return $this->convertToPhpCode([$this->paymentContext], $factoryAccessor); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasEnabledPaymentRules.php b/src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasEnabledPaymentRules.php new file mode 100644 index 000000000..ae5a062a1 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasEnabledPaymentRules.php @@ -0,0 +1,35 @@ +repository = $repository; + } + + /** + * {@inheritDoc} + */ + protected function getRulesByMethod($paymentMethodIdentifier) + { + return $this->repository->getEnabledRulesByMethod($paymentMethodIdentifier); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasPaymentRules.php b/src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasPaymentRules.php new file mode 100644 index 000000000..0e2cfedee --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Condition/PaymentMethodHasPaymentRules.php @@ -0,0 +1,35 @@ +repository = $repository; + } + + /** + * {@inheritDoc} + */ + protected function getRulesByMethod($paymentMethodIdentifier) + { + return $this->repository->getRulesByMethod($paymentMethodIdentifier); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/BasicPaymentContextBuilder.php b/src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/BasicPaymentContextBuilder.php new file mode 100644 index 000000000..ebe533217 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/BasicPaymentContextBuilder.php @@ -0,0 +1,258 @@ +sourceEntity = $sourceEntity; + $this->sourceEntityIdentifier = $sourceEntityIdentifier; + $this->paymentLineItemCollectionFactory = $paymentLineItemCollectionFactory; + } + + /** + * {@inheritDoc} + */ + public function getResult() + { + $params = $this->getMandatoryParams(); + $params += $this->getOptionalParams(); + + return new PaymentContext($params); + } + + /** + * {@inheritDoc} + */ + public function setLineItems(PaymentLineItemCollectionInterface $lineItemCollection) + { + $this->lineItems = $lineItemCollection->toArray(); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function addLineItem(PaymentLineItemInterface $paymentLineItem) + { + $this->lineItems[] = $paymentLineItem; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setBillingAddress(AddressInterface $billingAddress) + { + $this->billingAddress = $billingAddress; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setShippingAddress(AddressInterface $shippingAddress) + { + $this->shippingAddress = $shippingAddress; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setShippingOrigin(AddressInterface $shippingOrigin) + { + $this->shippingOrigin = $shippingOrigin; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setPaymentMethod($paymentMethod) + { + $this->paymentMethod = $paymentMethod; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setCustomer(Customer $customer) + { + $this->customer = $customer; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setCompany(Company $company) + { + $this->company = $company; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setSubTotal(Price $subTotal) + { + $this->subTotal = $subTotal; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setCurrency($currency) + { + $this->currency = $currency; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setTotal($total) + { + $this->total = $total; + + return $this; + } + + /** + * @return array + */ + private function getMandatoryParams() + { + $lineItems = $this->paymentLineItemCollectionFactory->createPaymentLineItemCollection($this->lineItems); + $params = [ + PaymentContext::FIELD_SOURCE_ENTITY => $this->sourceEntity, + PaymentContext::FIELD_SOURCE_ENTITY_ID => $this->sourceEntityIdentifier, + PaymentContext::FIELD_LINE_ITEMS => $lineItems, + ]; + + return $params; + } + + /** + * @return array + */ + private function getOptionalParams() + { + $optionalParams = [ + PaymentContext::FIELD_CURRENCY => $this->currency, + PaymentContext::FIELD_SUBTOTAL => $this->subTotal, + PaymentContext::FIELD_BILLING_ADDRESS => $this->billingAddress, + PaymentContext::FIELD_SHIPPING_ADDRESS => $this->shippingAddress, + PaymentContext::FIELD_PAYMENT_METHOD => $this->paymentMethod, + PaymentContext::FIELD_CUSTOMER => $this->customer, + PaymentContext::FIELD_COMPANY => $this->company, + PaymentContext::FIELD_SHIPPING_ORIGIN => $this->shippingOrigin, + PaymentContext::FIELD_TOTAL => $this->total, + ]; + + // Exclude NULL elements. + $optionalParams = array_diff_key($optionalParams, array_filter($optionalParams, 'is_null')); + + return $optionalParams; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactory.php b/src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactory.php new file mode 100644 index 000000000..c0325de2e --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactory.php @@ -0,0 +1,35 @@ +collectionFactory = $collectionFactory; + } + + /** + * {@inheritDoc} + */ + public function createPaymentContextBuilder($sourceEntity, $sourceEntityId) + { + return new BasicPaymentContextBuilder( + $sourceEntity, + $sourceEntityId, + $this->collectionFactory + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/Builder/Factory/PaymentContextBuilderFactoryInterface.php b/src/Marello/Bundle/PaymentBundle/Context/Builder/Factory/PaymentContextBuilderFactoryInterface.php new file mode 100644 index 000000000..c822930e0 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/Builder/Factory/PaymentContextBuilderFactoryInterface.php @@ -0,0 +1,16 @@ +decoratedProductLineItemFactory = $decoratedProductLineItemFactory; + } + + /** + * @inheritDoc + */ + public function convert(PaymentContextInterface $paymentContext) + { + $lineItems = $paymentContext->getLineItems()->toArray(); + + return [ + 'lineItems' => array_map(function (PaymentLineItemInterface $lineItem) use ($lineItems) { + return $this->decoratedProductLineItemFactory + ->createLineItemWithDecoratedProductByLineItem($lineItems, $lineItem); + }, $lineItems), + 'billingAddress' => $paymentContext->getBillingAddress(), + 'shippingAddress' => $paymentContext->getShippingAddress(), + 'shippingOrigin' => $paymentContext->getShippingOrigin(), + 'paymentMethod' => $paymentContext->getPaymentMethod(), + 'currency' => $paymentContext->getCurrency(), + 'subtotal' => $paymentContext->getSubtotal(), + 'customer' => $paymentContext->getCustomer(), + 'company' => $paymentContext->getCompany(), + 'total' => $paymentContext->getTotal(), + ]; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/Converter/PaymentContextToRulesValueConverterInterface.php b/src/Marello/Bundle/PaymentBundle/Context/Converter/PaymentContextToRulesValueConverterInterface.php new file mode 100644 index 000000000..4cbfd8fd9 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/Converter/PaymentContextToRulesValueConverterInterface.php @@ -0,0 +1,15 @@ +factories = $factories; + } + + /** + * {@inheritDoc} + */ + public function create($entityClass, $entityId) + { + return $this->getFactory($entityClass, $entityId)->create($entityClass, $entityId); + } + + /** + * {@inheritDoc} + */ + public function supports($entityClass, $entityId) + { + foreach ($this->factories as $factory) { + if ($factory->supports($entityClass, $entityId)) { + return true; + } + } + + return false; + } + + /** + * @param $entityClass + * @param $entityId + * + * @return SupportsEntityPaymentContextFactoryInterface + * + * @throws UnsupportedEntityException + */ + protected function getFactory($entityClass, $entityId) + { + foreach ($this->factories as $factory) { + if ($factory->supports($entityClass, $entityId)) { + return $factory; + } + } + + $msg = sprintf( + 'Could not find payment context factory for given entity class (%s) and id (%d)', + $entityClass, + $entityId + ); + throw new UnsupportedEntityException($msg); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/Factory/Exception/UnsupportedEntityException.php b/src/Marello/Bundle/PaymentBundle/Context/Factory/Exception/UnsupportedEntityException.php new file mode 100644 index 000000000..5b5a26b55 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/Factory/Exception/UnsupportedEntityException.php @@ -0,0 +1,7 @@ +quantity = $quantity; + $this->productHolder = $productHolder; + } + + /** + * {@inheritDoc} + */ + public function getResult() + { + $params = [ + PaymentLineItem::FIELD_QUANTITY => $this->quantity, + PaymentLineItem::FIELD_PRODUCT_HOLDER => $this->productHolder, + PaymentLineItem::FIELD_ENTITY_IDENTIFIER => $this->productHolder->getId(), + ]; + + if (null !== $this->product) { + $params[PaymentLineItem::FIELD_PRODUCT] = $this->product; + } + + if (null !== $this->productSku) { + $params[PaymentLineItem::FIELD_PRODUCT_SKU] = $this->productSku; + } + + if (null !== $this->weight) { + $params[PaymentLineItem::FIELD_WEIGHT] = $this->weight; + } + + if (null !== $this->price) { + $params[PaymentLineItem::FIELD_PRICE] = $this->price; + } + + return new PaymentLineItem($params); + } + + /** + * {@inheritDoc} + */ + public function setProduct(Product $product) + { + $this->product = $product; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setProductSku($sku) + { + $this->productSku = $sku; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setWeight($weight) + { + $this->weight = $weight; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setPrice(Price $price) + { + $this->price = $price; + + return $this; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactory.php b/src/Marello/Bundle/PaymentBundle/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactory.php new file mode 100644 index 000000000..564a19d0a --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactory.php @@ -0,0 +1,20 @@ +get(self::FIELD_COMPANY); + } + + /** + * {@inheritDoc} + */ + public function getCustomer() + { + return $this->get(self::FIELD_CUSTOMER); + } + + /** + * {@inheritDoc} + */ + public function getLineItems() + { + return $this->get(self::FIELD_LINE_ITEMS); + } + + /** + * {@inheritDoc} + */ + public function getBillingAddress() + { + return $this->get(self::FIELD_BILLING_ADDRESS); + } + + /** + * {@inheritDoc} + */ + public function getShippingAddress() + { + return $this->get(self::FIELD_SHIPPING_ADDRESS); + } + + /** + * {@inheritDoc} + */ + public function getShippingOrigin() + { + return $this->get(self::FIELD_SHIPPING_ORIGIN); + } + + /** + * {@inheritDoc} + */ + public function getPaymentMethod() + { + return $this->get(self::FIELD_PAYMENT_METHOD); + } + + /** + * {@inheritDoc} + */ + public function getCurrency() + { + return $this->get(self::FIELD_CURRENCY); + } + + /** + * {@inheritDoc} + */ + public function getSubtotal() + { + return $this->get(self::FIELD_SUBTOTAL); + } + + /** + * {@inheritDoc} + */ + public function getSourceEntity() + { + return $this->get(self::FIELD_SOURCE_ENTITY); + } + + /** + * {@inheritDoc} + */ + public function getSourceEntityIdentifier() + { + return $this->get(self::FIELD_SOURCE_ENTITY_ID); + } + + /** + * {@inheritDoc} + */ + public function getTotal() + { + return $this->get(self::FIELD_TOTAL); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/PaymentContextFactoryInterface.php b/src/Marello/Bundle/PaymentBundle/Context/PaymentContextFactoryInterface.php new file mode 100644 index 000000000..66b75f004 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/PaymentContextFactoryInterface.php @@ -0,0 +1,13 @@ +get(self::FIELD_PRICE); + } + + /** + * {@inheritDoc} + */ + public function getProduct() + { + return $this->get(self::FIELD_PRODUCT); + } + + /** + * {@inheritDoc} + */ + public function getProductHolder() + { + return $this->get(self::FIELD_PRODUCT_HOLDER); + } + + /** + * {@inheritDoc} + */ + public function getProductSku() + { + return $this->get(self::FIELD_PRODUCT_SKU); + } + + /** + * {@inheritDoc} + */ + public function getEntityIdentifier() + { + return $this->get(self::FIELD_ENTITY_IDENTIFIER); + } + + /** + * {@inheritDoc} + */ + public function getQuantity() + { + return $this->get(self::FIELD_QUANTITY); + } + + /** + * {@inheritDoc} + */ + public function getWeight() + { + return $this->get(self::FIELD_WEIGHT); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Context/PaymentLineItemInterface.php b/src/Marello/Bundle/PaymentBundle/Context/PaymentLineItemInterface.php new file mode 100644 index 000000000..377044bad --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Context/PaymentLineItemInterface.php @@ -0,0 +1,46 @@ + PaymentMethodsConfigsRule::class + ]; + } + + /** + * @Route("/create", name="marello_payment_methods_configs_rule_create") + * @Template("MarelloPaymentBundle:PaymentMethodsConfigsRule:update.html.twig") + * @Acl( + * id="marello_payment_methods_configs_rule_create", + * type="entity", + * permission="CREATE", + * class="MarelloPaymentBundle:PaymentMethodsConfigsRule" + * ) + * + * @param Request $request + * @return array + */ + public function createAction(Request $request) + { + return $this->update(new PaymentMethodsConfigsRule(), $request); + } + + /** + * @Route("/view/{id}", name="marello_payment_methods_configs_rule_view", requirements={"id"="\d+"}) + * @Template("MarelloPaymentBundle:PaymentMethodsConfigsRule:view.html.twig") + * @Acl( + * id="marello_payment_methods_configs_rule_view", + * type="entity", + * class="MarelloPaymentBundle:PaymentMethodsConfigsRule", + * permission="VIEW" + * ) + * + * @param PaymentMethodsConfigsRule $paymentMethodsConfigsRule + * + * @return array + */ + public function viewAction(PaymentMethodsConfigsRule $paymentMethodsConfigsRule) + { + return [ + 'entity' => $paymentMethodsConfigsRule, + ]; + } + + /** + * @Route("/update/{id}", name="marello_payment_methods_configs_rule_update", requirements={"id"="\d+"}) + * @Template("MarelloPaymentBundle:PaymentMethodsConfigsRule:update.html.twig") + * @Acl( + * id="marello_payment_methods_configs_rule_update", + * type="entity", + * permission="EDIT", + * class="MarelloPaymentBundle:PaymentMethodsConfigsRule" + * ) + * @param Request $request + * @param PaymentMethodsConfigsRule $entity + * + * @return array + */ + public function updateAction(Request $request, PaymentMethodsConfigsRule $entity) + { + return $this->update($entity, $request); + } + + /** + * @param PaymentMethodsConfigsRule $entity + * @param Request $request + * @return array|\Symfony\Component\HttpFoundation\RedirectResponse + */ + protected function update(PaymentMethodsConfigsRule $entity, Request $request) + { + $form = $this->createForm(PaymentMethodsConfigsRuleType::class); + if ($this->get('marello_payment.form.handler.payment_methods_configs_rule')->process($form, $entity)) { + $this->get('session')->getFlashBag()->add( + 'success', + $this->get('translator')->trans('marello.payment.controller.rule.saved.message') + ); + + return $this->get('oro_ui.router')->redirect($entity); + } + + if ($request->get(PaymentMethodsConfigsRuleHandler::UPDATE_FLAG, false)) { + // take different form due to JS validation should be shown even in case + // when it was not validated on backend + $form = $this->createForm(PaymentMethodsConfigsRuleType::class, $form->getData()); + } + + return [ + 'entity' => $entity, + 'form' => $form->createView() + ]; + } + + /** + * @Route("/{gridName}/massAction/{actionName}", name="marello_payment_methods_configs_massaction") + * @Acl( + * id="marello_payment_methods_configs_update", + * type="entity", + * permission="EDIT", + * class="MarelloPaymentBundle:PaymentMethodsConfigsRule" + * ) + * @CsrfProtection() + * + * @param string $gridName + * @param string $actionName + * @param Request $request + * + * @return JsonResponse + */ + public function markMassAction($gridName, $actionName, Request $request) + { + /** @var MassActionDispatcher $massActionDispatcher */ + $massActionDispatcher = $this->get('oro_datagrid.mass_action.dispatcher'); + + $response = $massActionDispatcher->dispatchByRequest($gridName, $actionName, $request); + + $data = [ + 'successful' => $response->isSuccessful(), + 'message' => $response->getMessage() + ]; + + return new JsonResponse(array_merge($data, $response->getOptions())); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/CompositePaymentMethodProviderCompilerPass.php b/src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/CompositePaymentMethodProviderCompilerPass.php new file mode 100644 index 000000000..a7d69982c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/CompositePaymentMethodProviderCompilerPass.php @@ -0,0 +1,34 @@ +hasDefinition(self::COMPOSITE_SERVICE)) { + return; + } + + $taggedServices = $container->findTaggedServiceIds(self::TAG); + if (empty($taggedServices)) { + return; + } + + $compiledServiceDefinition = $container->getDefinition(self::COMPOSITE_SERVICE); + + foreach ($taggedServices as $method => $value) { + $compiledServiceDefinition->addMethodCall('addProvider', [new Reference($method)]); + } + } +} diff --git a/src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/TwigSandboxConfigurationPass.php b/src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/TwigSandboxConfigurationPass.php new file mode 100644 index 000000000..b5ae7f0c5 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/DependencyInjection/Compiler/TwigSandboxConfigurationPass.php @@ -0,0 +1,43 @@ +root(MarelloPaymentExtension::ALIAS); + + SettingsBuilder::append( + $rootNode, + [ + // General + self::MERCHANT_COUNTRY_KEY => [ + 'type' => 'text', + 'value' => LocaleConfiguration::DEFAULT_COUNTRY, + ], + ] + ); + + return $treeBuilder; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/DependencyInjection/MarelloPaymentExtension.php b/src/Marello/Bundle/PaymentBundle/DependencyInjection/MarelloPaymentExtension.php new file mode 100644 index 000000000..b7ce323ad --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/DependencyInjection/MarelloPaymentExtension.php @@ -0,0 +1,36 @@ +processConfiguration($configuration, $configs); + $container->prependExtensionConfig($this->getAlias(), $config); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + $loader->load('mass_action.yml'); + $loader->load('form_types.yml'); + } + + /** + * {@inheritDoc} + */ + public function getAlias() + { + return self::ALIAS; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodAwareInterface.php b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodAwareInterface.php new file mode 100644 index 000000000..25a5bc018 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodAwareInterface.php @@ -0,0 +1,17 @@ +id; + } + + /** + * @return PaymentMethodsConfigsRule + */ + public function getMethodsConfigsRule() + { + return $this->methodsConfigsRule; + } + + /** + * @param PaymentMethodsConfigsRule $methodsConfigsRule + * @return $this + */ + public function setMethodsConfigsRule($methodsConfigsRule) + { + $this->methodsConfigsRule = $methodsConfigsRule; + + return $this; + } + + /** + * @return string + */ + public function getMethod() + { + return $this->method; + } + + /** + * @param string $method + * @return $this + */ + public function setMethod($method) + { + $this->method = $method; + + return $this; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param array $options + * @return $this + */ + public function setOptions($options) + { + $this->options = $options; + + return $this; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRule.php b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRule.php new file mode 100644 index 000000000..9ea57aa59 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRule.php @@ -0,0 +1,289 @@ +methodConfigs = new ArrayCollection(); + $this->destinations = new ArrayCollection(); + } + + /** + * {@inheritdoc} + */ + public function getRule() + { + return $this->rule; + } + + /** + * @param RuleInterface $rule + * + * @return $this + */ + public function setRule(RuleInterface $rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * @return Collection|PaymentMethodConfig[] + */ + public function getMethodConfigs() + { + return $this->methodConfigs; + } + + /** + * @param PaymentMethodConfig $methodConfig + * + * @return bool + */ + public function hasMethodConfig(PaymentMethodConfig $methodConfig) + { + return $this->methodConfigs->contains($methodConfig); + } + + /** + * @param PaymentMethodConfig $methodConfig + * + * @return $this + */ + public function addMethodConfig(PaymentMethodConfig $methodConfig) + { + if (!$this->hasMethodConfig($methodConfig)) { + $this->methodConfigs[] = $methodConfig; + $methodConfig->setMethodsConfigsRule($this); + } + + return $this; + } + + /** + * @param PaymentMethodConfig $methodConfig + * + * @return $this + */ + public function removeMethodConfig(PaymentMethodConfig $methodConfig) + { + if ($this->hasMethodConfig($methodConfig)) { + $this->methodConfigs->removeElement($methodConfig); + } + + return $this; + } + + /** + * @return Collection|PaymentMethodsConfigsRuleDestination[] + */ + public function getDestinations() + { + return $this->destinations; + } + + /** + * @param PaymentMethodsConfigsRuleDestination $destination + * + * @return $this + */ + public function addDestination(PaymentMethodsConfigsRuleDestination $destination) + { + if (!$this->destinations->contains($destination)) { + $this->destinations->add($destination); + $destination->setMethodsConfigsRule($this); + } + + return $this; + } + + /** + * @param PaymentMethodsConfigsRuleDestination $destination + * + * @return $this + */ + public function removeDestination(PaymentMethodsConfigsRuleDestination $destination) + { + if ($this->destinations->contains($destination)) { + $this->destinations->removeElement($destination); + } + + return $this; + } + + /** + * @return string + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @param string $currency + * + * @return $this + */ + public function setCurrency($currency) + { + $this->currency = $currency; + + return $this; + } + + /** + * @return OrganizationInterface + */ + public function getOrganization() + { + return $this->organization; + } + + /** + * @param OrganizationInterface $organization + * + * @return $this + */ + public function setOrganization(OrganizationInterface $organization) + { + $this->organization = $organization; + + return $this; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestination.php b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestination.php new file mode 100644 index 000000000..7c2bad3f8 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestination.php @@ -0,0 +1,314 @@ +postalCodes = new ArrayCollection(); + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * @return PaymentMethodsConfigsRule + */ + public function getMethodsConfigsRule() + { + return $this->methodsConfigsRule; + } + + /** + * @param PaymentMethodsConfigsRule $methodsConfigsRule + * @return $this + */ + public function setMethodsConfigsRule($methodsConfigsRule) + { + $this->methodsConfigsRule = $methodsConfigsRule; + + return $this; + } + + /** + * @return Collection|PaymentMethodsConfigsRuleDestinationPostalCode[] + */ + public function getPostalCodes() + { + return $this->postalCodes; + } + + /** + * @param PaymentMethodsConfigsRuleDestinationPostalCode $postalCode + * @return $this + */ + public function addPostalCode(PaymentMethodsConfigsRuleDestinationPostalCode $postalCode) + { + if (!$this->postalCodes->contains($postalCode)) { + $postalCode->setDestination($this); + $this->postalCodes->add($postalCode); + } + + return $this; + } + + /** + * @param PaymentMethodsConfigsRuleDestinationPostalCode $postalCode + * @return $this + */ + public function removePostalCode(PaymentMethodsConfigsRuleDestinationPostalCode $postalCode) + { + if ($this->postalCodes->contains($postalCode)) { + $this->postalCodes->removeElement($postalCode); + } + + return $this; + } + + /** + * @return Region + */ + public function getRegion() + { + return $this->region; + } + + /** + * @param Region $region + * @return $this + */ + public function setRegion($region) + { + $this->region = $region; + + return $this; + } + + /** + * Get name of region + * + * @return string + */ + public function getRegionName() + { + return $this->getRegion() ? $this->getRegion()->getName() : ''; + } + + /** + * Get code of region + * + * @return string + */ + public function getRegionCode() + { + return $this->getRegion() ? $this->getRegion()->getCode() : ''; + } + + /** + * @return string + */ + public function getRegionText() + { + return $this->regionText; + } + + /** + * @param string $regionText + * @return $this + */ + public function setRegionText($regionText) + { + $this->regionText = $regionText; + + return $this; + } + + /** + * @return Country + */ + public function getCountry() + { + return $this->country; + } + + /** + * Get name of country + * + * @return string + */ + public function getCountryName() + { + return $this->getCountry() ? $this->getCountry()->getName() : ''; + } + + /** + * @param Country $country + * @return $this + */ + public function setCountry($country) + { + $this->country = $country; + + return $this; + } + + /** + * Get country ISO2 code + * + * @return string + */ + public function getCountryIso2() + { + return $this->getCountry() ? $this->getCountry()->getIso2Code() : ''; + } + + /** + * Get country ISO3 code + * + * @return string + */ + public function getCountryIso3() + { + return $this->getCountry() ? $this->getCountry()->getIso3Code() : ''; + } + + /** + * Convert address to string + * + * @return string + */ + public function __toString() + { + $countryPostalStr = implode( + ' ', + array_filter([ + $this->getCountry(), + implode(', ', array_map(function (PaymentMethodsConfigsRuleDestinationPostalCode $postalCode) { + return (string)$postalCode; + }, $this->postalCodes->getValues())), + ]) + ); + + return implode(', ', array_filter([$this->getRegionName(), $countryPostalStr])); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestinationPostalCode.php b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestinationPostalCode.php new file mode 100644 index 000000000..c5576c56a --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Entity/PaymentMethodsConfigsRuleDestinationPostalCode.php @@ -0,0 +1,125 @@ +id; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * @return PaymentMethodsConfigsRuleDestination + */ + public function getDestination() + { + return $this->destination; + } + + /** + * @param PaymentMethodsConfigsRuleDestination $destination + * @return $this + */ + public function setDestination(PaymentMethodsConfigsRuleDestination $destination) + { + $this->destination = $destination; + + return $this; + } + + /** + * @return mixed + */ + public function __toString() + { + return (string)$this->getName(); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodConfigRepository.php b/src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodConfigRepository.php new file mode 100644 index 000000000..3ef96626d --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodConfigRepository.php @@ -0,0 +1,49 @@ +createQueryBuilder('methodConfig'); + + $qb->delete() + ->where( + $qb->expr()->eq('methodConfig.method', ':method') + ) + ->setParameter('method', $method); + + $qb->getQuery()->execute(); + } + + /** + * @param array $ids + */ + public function deleteByIds(array $ids) + { + $qb = $this->createQueryBuilder('methodConfig'); + $qb->delete() + ->where($qb->expr()->in('methodConfig.id', ':ids')) + ->setParameter('ids', $ids) + ->getQuery()->execute(); + } + + /** + * @param string|string[] $method + * + * @return PaymentMethodConfig[] + */ + public function findByMethod($method) + { + return $this->findBy([ + 'method' => $method + ]); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodsConfigsRuleRepository.php b/src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodsConfigsRuleRepository.php new file mode 100644 index 000000000..8a65b1626 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Entity/Repository/PaymentMethodsConfigsRuleRepository.php @@ -0,0 +1,188 @@ +aclHelper = $aclHelper; + } + + /** + * @param AddressInterface $billingAddress + * @param string $currency + * + * @return PaymentMethodsConfigsRule[] + */ + public function getByDestinationAndCurrency( + AddressInterface $billingAddress, + string $currency + ): array { + $queryBuilder = $this->getByCurrencyQueryBuilder($currency) + ->leftJoin('methodsConfigsRule.destinations', 'destination') + ->leftJoin('methodsConfigsRule.rule', 'rule') + ->addSelect('rule', 'destination', 'postalCode') + ->leftJoin('destination.region', 'region') + ->leftJoin('destination.postalCodes', 'postalCode') + ->andWhere('destination.country = :country or destination.country is null') + ->andWhere('region.code = :regionCode or region.code is null') + ->andWhere('postalCode.name in (:postalCodes) or postalCode.name is null') + ->setParameter('country', $billingAddress->getCountryIso2()) + ->setParameter('regionCode', $billingAddress->getRegionCode()) + ->setParameter('postalCodes', explode(',', $billingAddress->getPostalCode())); + + return $this->aclHelper->apply($queryBuilder)->getResult(); + } + + /** + * @param string $currency + * + * @return PaymentMethodsConfigsRule[] + */ + public function getByCurrency(string $currency): array + { + $query = $this->getByCurrencyQueryBuilder($currency); + + return $this->aclHelper->apply($query)->getResult(); + } + + /** + * @param string $currency + * + * @return PaymentMethodsConfigsRule[] + */ + public function getByCurrencyWithoutDestination(string $currency): array + { + $query = $this->getByCurrencyQueryBuilder($currency) + ->leftJoin('methodsConfigsRule.destinations', 'destination') + ->andWhere('destination.id is null'); + + return $this->aclHelper->apply($query)->getResult(); + } + + /** + * @param string $methodId + * + * @return PaymentMethodsConfigsRule[] + */ + public function getConfigsWithEnabledRuleAndMethod($methodId) + { + $query = $this->createQueryBuilder('methodsConfigsRule') + ->innerJoin('methodsConfigsRule.methodConfigs', 'methodConfigs') + ->innerJoin('methodsConfigsRule.rule', 'rule') + ->andWhere('rule.enabled = true') + ->andWhere('methodConfigs.method = :methodId') + ->setParameter('methodId', $methodId); + + return $this->aclHelper->apply($query)->getResult(); + } + + /** + * @param bool $onlyEnabled + * + * @return mixed + */ + public function getRulesWithoutPaymentMethods($onlyEnabled = false) + { + $qb = $this->createQueryBuilder('methodsConfigsRule') + ->select('rule.id') + ->leftJoin('methodsConfigsRule.methodConfigs', 'methodConfigs') + ->leftJoin('methodsConfigsRule.rule', 'rule'); + if ($onlyEnabled) { + $qb->andWhere('rule.enabled = true'); + } + + return $qb + ->having('COUNT(methodConfigs.id) = 0') + ->groupBy('rule.id') + ->getQuery()->execute(); + } + + public function disableRulesWithoutPaymentMethods() + { + $rules = $this->getRulesWithoutPaymentMethods(true); + if (0 < count($rules)) { + $enabledRulesIds = array_column($rules, 'id'); + $qb = $this->createQueryBuilder('methodsConfigsRule'); + $qb->update('MarelloRuleBundle:Rule', 'rule') + ->set('rule.enabled', ':newValue') + ->setParameter('newValue', false) + ->where($qb->expr()->in('rule.id', ':rules')) + ->setParameter('rules', $enabledRulesIds) + ->getQuery()->execute(); + } + } + + /** + * @param string $currency + * + * @return QueryBuilder + */ + private function getByCurrencyQueryBuilder($currency): QueryBuilder + { + $queryBuilder = $this->createQueryBuilder('methodsConfigsRule'); + + return $queryBuilder + ->leftJoin('methodsConfigsRule.methodConfigs', 'methodConfigs') + ->where('methodsConfigsRule.currency = :currency') + ->orderBy($queryBuilder->expr()->asc('methodsConfigsRule.id')) + ->setParameter('currency', $currency); + } + + /** + * @param string $method + * + * @return PaymentMethodsConfigsRule[] + */ + public function getRulesByMethod($method) + { + $qb = $this->getRulesByMethodQueryBuilder($method); + + return $this->aclHelper->apply($qb)->getResult(); + } + + /** + * @param string $method + * + * @return PaymentMethodsConfigsRule[] + */ + public function getEnabledRulesByMethod($method) + { + $qb = $this->getRulesByMethodQueryBuilder($method) + ->innerJoin('methodsConfigsRule.rule', 'rule', Expr\Join::WITH, 'rule.enabled = true'); + + return $this->aclHelper->apply($qb)->getResult(); + } + + /** + * @param string $method + * + * @return QueryBuilder + */ + private function getRulesByMethodQueryBuilder($method) + { + return $this->createQueryBuilder('methodsConfigsRule') + ->innerJoin('methodsConfigsRule.methodConfigs', 'methodConfigs') + ->where('methodConfigs.method = :method') + ->setParameter('method', $method); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Event/ApplicablePaymentMethodViewEvent.php b/src/Marello/Bundle/PaymentBundle/Event/ApplicablePaymentMethodViewEvent.php new file mode 100644 index 000000000..ffeea75ba --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Event/ApplicablePaymentMethodViewEvent.php @@ -0,0 +1,121 @@ +paymentContext = $paymentContext; + $this->methodId = $methodId; + $this->methodLabel = $methodLabel; + $this->options = $options; + } + + /** + * @return string + */ + public function getMethodId() + { + return $this->methodId; + } + + /** + * @param string $methodId + * @return ApplicablePaymentMethodViewEvent + */ + public function setMethodId($methodId) + { + $this->methodId = $methodId; + + return $this; + } + + /** + * @return string + */ + public function getMethodLabel() + { + return $this->methodLabel; + } + + /** + * @param string $methodLabel + * @return ApplicablePaymentMethodViewEvent + */ + public function setMethodLabel($methodLabel) + { + $this->methodLabel = $methodLabel; + + return $this; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param array $options + * @return ApplicablePaymentMethodViewEvent + */ + public function setOptions($options) + { + $this->options = $options; + + return $this; + } + + /** + * @return PaymentContextInterface + */ + public function getPaymentContext() + { + return $this->paymentContext; + } + + /** + * @param PaymentContextInterface $paymentContext + * @return ApplicablePaymentMethodViewEvent + */ + public function setPaymentContext($paymentContext) + { + $this->paymentContext = $paymentContext; + + return $this; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Event/PaymentMethodConfigDataEvent.php b/src/Marello/Bundle/PaymentBundle/Event/PaymentMethodConfigDataEvent.php new file mode 100755 index 000000000..299e9673d --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Event/PaymentMethodConfigDataEvent.php @@ -0,0 +1,56 @@ +methodIdentifier = $identifier; + } + + /** + * @return int|string + */ + public function getMethodIdentifier() + { + return $this->methodIdentifier; + } + + /** + * Returns payment method config template + * + * @return string + */ + public function getTemplate() + { + return $this->template; + } + + /** + * @param string $template + * @return $this + */ + public function setTemplate($template) + { + $this->template = $template; + return $this; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/EventListener/PaymentRuleViewMethodTemplateListener.php b/src/Marello/Bundle/PaymentBundle/EventListener/PaymentRuleViewMethodTemplateListener.php new file mode 100644 index 000000000..0c3554149 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/EventListener/PaymentRuleViewMethodTemplateListener.php @@ -0,0 +1,39 @@ +template = $template; + $this->provider = $provider; + } + + /** + * @param PaymentMethodConfigDataEvent $event + */ + public function onGetConfigData(PaymentMethodConfigDataEvent $event) + { + if ($this->provider->hasPaymentMethod($event->getMethodIdentifier())) { + $event->setTemplate($this->template); + } + } +} diff --git a/src/Marello/Bundle/PaymentBundle/ExpressionLanguage/DecoratedProductLineItemFactory.php b/src/Marello/Bundle/PaymentBundle/ExpressionLanguage/DecoratedProductLineItemFactory.php new file mode 100644 index 000000000..21e1c9b99 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/ExpressionLanguage/DecoratedProductLineItemFactory.php @@ -0,0 +1,48 @@ +virtualFieldsProductDecoratorFactory = $virtualFieldsProductDecoratorFactory; + } + + /** + * @param PaymentLineItemInterface[] $lineItems + * @param PaymentLineItemInterface $lineItem + * + * @return PaymentLineItem + */ + public function createLineItemWithDecoratedProductByLineItem(array $lineItems, PaymentLineItemInterface $lineItem) + { + $product = $lineItem->getProduct(); + + $decoratedProduct = $product + ? $this->virtualFieldsProductDecoratorFactory->createDecoratedProductByProductHolders($lineItems, $product) + : null; + + return new PaymentLineItem( + [ + PaymentLineItem::FIELD_PRICE => $lineItem->getPrice(), + PaymentLineItem::FIELD_QUANTITY => $lineItem->getQuantity(), + PaymentLineItem::FIELD_PRODUCT_HOLDER => $lineItem->getProductHolder(), + PaymentLineItem::FIELD_PRODUCT_SKU => $lineItem->getProductSku(), + PaymentLineItem::FIELD_PRODUCT => $decoratedProduct, + ] + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/DataTransformer/DestinationPostalCodeTransformer.php b/src/Marello/Bundle/PaymentBundle/Form/DataTransformer/DestinationPostalCodeTransformer.php new file mode 100644 index 000000000..deb9449d6 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/DataTransformer/DestinationPostalCodeTransformer.php @@ -0,0 +1,52 @@ +getName() . ', '; + } + $postalCodesString = rtrim($postalCodesString, ', '); + + return $postalCodesString; + } + + /** + * @param string|null $postalCodesString + * @return ArrayCollection|PaymentMethodsConfigsRuleDestinationPostalCode[] + */ + public function reverseTransform($postalCodesString) + { + $postalCodes = new ArrayCollection(); + + if (!$postalCodesString || $postalCodesString === '') { + return $postalCodes; + } + + $postalCodeNames = explode(',', $postalCodesString); + foreach ($postalCodeNames as $postalCodeName) { + $postalCode = new PaymentMethodsConfigsRuleDestinationPostalCode(); + + $postalCode->setName(trim($postalCodeName)); + $postalCodes->add($postalCode); + } + + return $postalCodes; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/DestinationCollectionTypeSubscriber.php b/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/DestinationCollectionTypeSubscriber.php new file mode 100644 index 000000000..b6688fe17 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/DestinationCollectionTypeSubscriber.php @@ -0,0 +1,43 @@ + 'preSubmit' + ]; + } + + /** + * @param FormEvent $event + */ + public function preSubmit(FormEvent $event) + { + $data = $event->getData(); + + if (!$data || + !(is_array($data) && array_key_exists('destinations', $data)) || + !is_array($data['destinations']) + ) { + return; + } + + foreach ($data['destinations'] as $index => $destination) { + if (!array_filter($destination)) { + unset($data['destinations']); + } + } + + $event->setData($data); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigCollectionSubscriber.php b/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigCollectionSubscriber.php new file mode 100755 index 000000000..583d53d5c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigCollectionSubscriber.php @@ -0,0 +1,87 @@ +paymentMethodProvider = $paymentMethodProvider; + } + + /** + * @return array + */ + public static function getSubscribedEvents() + { + return [ + FormEvents::PRE_SET_DATA => 'preSet', + FormEvents::PRE_SUBMIT => 'preSubmit', + ]; + } + + /** + * @param FormEvent $event + */ + public function preSet(FormEvent $event) + { + /** @var Collection|PaymentMethodConfig[] $data */ + $data = $event->getData(); + $form = $event->getForm(); + + if (!$data) { + return; + } + + foreach ($data as $index => $methodConfig) { + $paymentMethod = $this->paymentMethodProvider->getPaymentMethod($methodConfig->getMethod()); + if (!$paymentMethod) { + $data->remove($index); + $form->remove($index); + } + } + $event->setData($data); + } + + /** + * @param FormEvent $event + */ + public function preSubmit(FormEvent $event) + { + /** @var array $data */ + $submittedData = $event->getData(); + $form = $event->getForm(); + + if (!$submittedData) { + return; + } + + $filteredSubmittedData = []; + foreach ($submittedData as $index => $itemData) { + if (array_key_exists('method', $itemData) + && $this->paymentMethodProvider->getPaymentMethod($itemData['method']) !== null + ) { + $filteredSubmittedData[$index] = $itemData; + } else { + $form->remove($index); + } + } + + $event->setData($filteredSubmittedData); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigSubscriber.php b/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigSubscriber.php new file mode 100755 index 000000000..6b491e79c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/EventSubscriber/MethodConfigSubscriber.php @@ -0,0 +1,89 @@ +factory = $factory; + $this->paymentMethodProvider = $paymentMethodProvider; + } + + /** + * @return array + */ + public static function getSubscribedEvents() + { + return [ + FormEvents::PRE_SET_DATA => 'preSet', + FormEvents::PRE_SUBMIT => 'preSubmit', + ]; + } + + /** + * @param FormEvent $event + */ + public function preSet(FormEvent $event) + { + /** @var PaymentMethodConfig $data */ + $data = $event->getData(); + if (!$data) { + return; + } + $this->recreateDynamicChildren($event->getForm(), $data->getMethod()); + } + + /** + * @param FormEvent $event + */ + public function preSubmit(FormEvent $event) + { + $submittedData = $event->getData(); + $form = $event->getForm(); + /** @var PaymentMethodConfig $data */ + $data = $form->getData(); + + if (!$data) { + $this->recreateDynamicChildren($form, $submittedData['method']); + $event->setData($submittedData); + } + } + + /** + * @param FormInterface $form + * @param string $method + */ + protected function recreateDynamicChildren(FormInterface $form, $method) + { + $paymentMethod = $this->paymentMethodProvider->getPaymentMethod($method); + + $oldOptions = $form->get('options')->getConfig()->getOptions(); + $child = $this->factory->createNamed('options', $paymentMethod->getOptionsConfigurationFormType()); + $form->add('options', $paymentMethod->getOptionsConfigurationFormType(), array_merge($oldOptions, [ + 'compound' => $child->getConfig()->getOptions()['compound'] + ])); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/Handler/PaymentMethodsConfigsRuleHandler.php b/src/Marello/Bundle/PaymentBundle/Form/Handler/PaymentMethodsConfigsRuleHandler.php new file mode 100644 index 000000000..013782148 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/Handler/PaymentMethodsConfigsRuleHandler.php @@ -0,0 +1,62 @@ +requestStack = $requestStack; + $this->em = $em; + } + + /** + * @param FormInterface $form + * @param PaymentMethodsConfigsRule $entity + * @return bool + */ + public function process(FormInterface $form, PaymentMethodsConfigsRule $entity) + { + $form->setData($entity); + + $request = $this->requestStack->getCurrentRequest(); + if (in_array($request->getMethod(), ['POST', 'PUT'], true)) { + $this->submitPostPutRequest($form, $request); + if (!$request->get(self::UPDATE_FLAG, false) && $form->isValid()) { + $this->em->persist($entity); + $this->em->flush(); + + return true; + } + } + + return false; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigCollectionType.php b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigCollectionType.php new file mode 100644 index 000000000..bb378dbde --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigCollectionType.php @@ -0,0 +1,73 @@ +subscriber = $subscriber; + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber($this->subscriber); + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'show_form_when_empty' => false, + 'allow_add' => true, + 'entry_type' => PaymentMethodConfigType::class, + ]); + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + $view->vars['allow_add'] = false; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return CollectionType::class; + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigType.php b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigType.php new file mode 100644 index 000000000..25c73d2b8 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodConfigType.php @@ -0,0 +1,87 @@ +subscriber = $subscriber; + $this->methodProvider = $methodProvider; + } + + /** + * {@inheritDoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('method', HiddenType::class, ['required' => true]); + $builder->add('options', HiddenType::class); + + $builder->addEventSubscriber($this->subscriber); + } + + /** + * {@inheritDoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $methodsLabels = []; + $methodsIcons = []; + /* @var PaymentMethodInterface|PaymentMethodIconAwareInterface $method */ + foreach ($this->methodProvider->getPaymentMethods() as $method) { + $methodsLabels[$method->getIdentifier()] = $method->getLabel(); + $methodsIcons[$method->getIdentifier()] = $method->getIcon(); + } + $view->vars['methods_labels'] = $methodsLabels; + $view->vars['methods_icons'] = $methodsIcons; + } + + /** + * @param OptionsResolver $resolver + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => PaymentMethodConfig::class, + ]); + } + + /** + * {@inheritDoc} + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodSelectType.php b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodSelectType.php new file mode 100644 index 000000000..2261e9248 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodSelectType.php @@ -0,0 +1,62 @@ +paymentMethodChoicesProvider = $paymentMethodChoicesProvider; + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefaults([ + 'expanded' => true, + 'choices' => array_flip($this->getChoices()), + ]); + } + + /** + * @return array + */ + protected function getChoices() + { + return $this->paymentMethodChoicesProvider->getMethods(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return ChoiceType::class; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleDestinationType.php b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleDestinationType.php new file mode 100644 index 000000000..25dfa374c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleDestinationType.php @@ -0,0 +1,89 @@ +subscriber = $subscriber; + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber($this->subscriber); + $builder->add('country', CountryType::class, [ + 'required' => true, + 'label' => 'oro.address.country.label', + //Used to show validation error for empty countries while creating rules + 'constraints' => new NotBlank(), + ]) + ->add('region', RegionType::class, ['required' => false, 'label' => 'oro.address.region.label']) + ->add( + 'region_text', + HiddenType::class, + ['required' => false, 'random_id' => true, 'label' => 'oro.address.region_text.label'] + ) + ->add('postalCodes', TextType::class, ['required' => false, 'label' => 'oro.address.postal_code.label']); + + $builder->get('postalCodes')->addModelTransformer(new DestinationPostalCodeTransformer()); + } + + /** + * @param OptionsResolver $resolver + * @throws AccessException + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => PaymentMethodsConfigsRuleDestination::class, + 'region_route' => 'oro_api_country_get_regions', + ]); + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + if (!empty($options['region_route'])) { + $view->vars['region_route'] = $options['region_route']; + } + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleType.php b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleType.php new file mode 100644 index 000000000..92087712d --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Form/Type/PaymentMethodsConfigsRuleType.php @@ -0,0 +1,95 @@ +provider = $provider; + $this->translator = $translator; + } + + /** + * {@inheritDoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('rule', RuleType::class, ['label' => 'marello.payment.paymentmethodsconfigsrule.rule.label']) + ->add('currency', CurrencySelectionType::class, [ + 'label' => 'marello.payment.paymentmethodsconfigsrule.currency.label', + 'placeholder' => 'oro.currency.currency.form.choose', + ]) + ->add('destinations', CollectionType::class, [ + 'required' => false, + 'entry_type' => PaymentMethodsConfigsRuleDestinationType::class, + 'label' => 'marello.payment.paymentmethodsconfigsrule.destinations.label', + 'show_form_when_empty' => false, + ]) + ->add('methodConfigs', PaymentMethodConfigCollectionType::class, [ + 'required' => false, + ]) + ->add('method', OroChoiceType::class, [ + 'mapped' => false, + 'choices' => array_flip($this->provider->getMethods()) + ]); + } + + /** + * {@inheritDoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['methods'] = $this->provider->getMethods(true); + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => PaymentMethodsConfigsRule::class + ]); + } + + /** + * {@inheritDoc} + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Formatter/PaymentMethodLabelFormatter.php b/src/Marello/Bundle/PaymentBundle/Formatter/PaymentMethodLabelFormatter.php new file mode 100755 index 000000000..4f2bbbbad --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Formatter/PaymentMethodLabelFormatter.php @@ -0,0 +1,33 @@ +paymentMethodProvider = $paymentMethodProvider; + } + + + /** + * @param string $paymentMethodName + * @return string + */ + public function formatPaymentMethodLabel($paymentMethodName) + { + $paymentMethod = $this->paymentMethodProvider->getPaymentMethod($paymentMethodName); + + return $paymentMethod->getLabel(); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/MarelloPaymentBundle.php b/src/Marello/Bundle/PaymentBundle/MarelloPaymentBundle.php new file mode 100644 index 000000000..f16f2f926 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/MarelloPaymentBundle.php @@ -0,0 +1,27 @@ +addCompilerPass(new TwigSandboxConfigurationPass()); + $container->addCompilerPass(new CompositePaymentMethodProviderCompilerPass()); + + parent::build($container); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Config/ParameterBag/AbstractParameterBagPaymentConfig.php b/src/Marello/Bundle/PaymentBundle/Method/Config/ParameterBag/AbstractParameterBagPaymentConfig.php new file mode 100644 index 000000000..62497252a --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Config/ParameterBag/AbstractParameterBagPaymentConfig.php @@ -0,0 +1,28 @@ +get(self::FIELD_LABEL); + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethodIdentifier() + { + return $this->get(self::FIELD_PAYMENT_METHOD_IDENTIFIER); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Config/PaymentConfigInterface.php b/src/Marello/Bundle/PaymentBundle/Method/Config/PaymentConfigInterface.php new file mode 100644 index 000000000..7b3ce9163 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Config/PaymentConfigInterface.php @@ -0,0 +1,16 @@ +eventDispatcher = $eventDispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($id) + { + $this->eventDispatcher->dispatch(MethodRemovalEvent::NAME, new MethodRemovalEvent($id)); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Event/BasicMethodRenamingEventDispatcher.php b/src/Marello/Bundle/PaymentBundle/Method/Event/BasicMethodRenamingEventDispatcher.php new file mode 100644 index 000000000..ddac0181a --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Event/BasicMethodRenamingEventDispatcher.php @@ -0,0 +1,29 @@ +eventDispatcher = $eventDispatcher; + } + + /** + * {@inheritDoc} + */ + public function dispatch($oldId, $newId) + { + $this->eventDispatcher->dispatch(MethodRenamingEvent::NAME, new MethodRenamingEvent($oldId, $newId)); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEvent.php b/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEvent.php new file mode 100644 index 000000000..b0e03deb9 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEvent.php @@ -0,0 +1,31 @@ +methodId = $id; + } + + /** + * @return int|string + */ + public function getMethodIdentifier() + { + return $this->methodId; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEventDispatcherInterface.php b/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEventDispatcherInterface.php new file mode 100644 index 000000000..a3023dab6 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRemovalEventDispatcherInterface.php @@ -0,0 +1,12 @@ +oldMethodId = $oldId; + $this->newMethodId = $newId; + } + + /** + * @return string + */ + public function getOldMethodIdentifier() + { + return $this->oldMethodId; + } + + /** + * @return string + */ + public function getNewMethodIdentifier() + { + return $this->newMethodId; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRenamingEventDispatcherInterface.php b/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRenamingEventDispatcherInterface.php new file mode 100644 index 000000000..3c5802158 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Event/MethodRenamingEventDispatcherInterface.php @@ -0,0 +1,14 @@ +channelType = $channelType; + $this->identifierGenerator = $identifierGenerator; + $this->dispatcher = $dispatcher; + } + + /** + * @param ChannelDeleteEvent $event + */ + public function onRemove(ChannelDeleteEvent $event) + { + $channel = $event->getChannel(); + if ($this->channelType === $channel->getType()) { + $this->dispatcher->dispatch($this->identifierGenerator->generateIdentifier($channel)); + } + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRemovalListener.php b/src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRemovalListener.php new file mode 100644 index 000000000..c6c870a5e --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRemovalListener.php @@ -0,0 +1,77 @@ +doctrineHelper = $doctrineHelper; + $this->logger = $logger; + } + + /** + * @param MethodRemovalEvent $event + * @throws \Exception + */ + public function onMethodRemove(MethodRemovalEvent $event) + { + $methodId = $event->getMethodIdentifier(); + $connection = $this->getEntityManager()->getConnection(); + try { + $connection->beginTransaction(); + $this->getPaymentMethodConfigRepository()->deleteByMethod($methodId); + $this->getPaymentMethodsConfigsRuleRepository()->disableRulesWithoutPaymentMethods(); + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + $this->logger->critical($e->getMessage(), [ + 'payment_method_identifier' => $methodId + ]); + } + } + + /** + * @return \Doctrine\ORM\EntityManager|null + */ + private function getEntityManager() + { + return $this->doctrineHelper->getEntityManagerForClass('MarelloPaymentBundle:PaymentMethodsConfigsRule'); + } + + /** + * @return PaymentMethodConfigRepository|\Doctrine\ORM\EntityRepository + */ + private function getPaymentMethodConfigRepository() + { + return $this->doctrineHelper->getEntityRepository('MarelloPaymentBundle:PaymentMethodConfig'); + } + + /** + * @return PaymentMethodsConfigsRuleRepository|\Doctrine\ORM\EntityRepository + */ + private function getPaymentMethodsConfigsRuleRepository() + { + return $this->doctrineHelper->getEntityRepository('MarelloPaymentBundle:PaymentMethodsConfigsRule'); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRenamingListener.php b/src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRenamingListener.php new file mode 100644 index 000000000..58c2db56a --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/EventListener/MethodRenamingListener.php @@ -0,0 +1,43 @@ +paymentMethodConfigRepository = $paymentMethodConfigRepository; + } + + /** + * @param MethodRenamingEvent $event + */ + public function onMethodRename(MethodRenamingEvent $event) + { + $this->updateRuleConfigs($event->getOldMethodIdentifier(), $event->getNewMethodIdentifier()); + } + + /** + * @param string $oldId + * @param string $newId + */ + private function updateRuleConfigs($oldId, $newId) + { + $configs = $this->paymentMethodConfigRepository->findByMethod($oldId); + foreach ($configs as $config) { + $config->setMethod($newId); + } + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/EventListener/PaymentMethodDisableIntegrationListener.php b/src/Marello/Bundle/PaymentBundle/Method/EventListener/PaymentMethodDisableIntegrationListener.php new file mode 100644 index 000000000..8d223e2f2 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/EventListener/PaymentMethodDisableIntegrationListener.php @@ -0,0 +1,48 @@ +channelType = $channelType; + $this->methodIdentifierGenerator = $methodIdentifierGenerator; + $this->paymentMethodDisableHandler = $paymentMethodDisableHandler; + } + + /** + * @param ChannelDisableEvent $event + */ + public function onIntegrationDisable(ChannelDisableEvent $event) + { + $channel = $event->getChannel(); + $channelType = $channel->getType(); + if ($channelType === $this->channelType) { + $methodId = $this->methodIdentifierGenerator->generateIdentifier($channel); + $this->paymentMethodDisableHandler->handleMethodDisable($methodId); + } + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Factory/IntegrationPaymentMethodFactoryInterface.php b/src/Marello/Bundle/PaymentBundle/Method/Factory/IntegrationPaymentMethodFactoryInterface.php new file mode 100644 index 000000000..0aa06f53c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Factory/IntegrationPaymentMethodFactoryInterface.php @@ -0,0 +1,15 @@ +handler = $handler; + $this->repository = $repository; + $this->paymentMethodProvider = $paymentMethodProvider; + } + + /** + * {@inheritDoc} + */ + public function handleMethodDisable($methodId) + { + $this->handler->handleMethodDisable($methodId); + $paymentMethodsConfigsRule = $this->repository->getEnabledRulesByMethod($methodId); + foreach ($paymentMethodsConfigsRule as $configRule) { + if (!$this->configHasEnabledMethod($configRule, $methodId)) { + $rule = $configRule->getRule(); + $rule->setEnabled(false); + } + } + } + + /** + * @param PaymentMethodsConfigsRule $configRule + * @param string $disabledMethodId + * + * @return bool + */ + private function configHasEnabledMethod(PaymentMethodsConfigsRule $configRule, $disabledMethodId) + { + $methodConfigs = $configRule->getMethodConfigs(); + foreach ($methodConfigs as $methodConfig) { + $methodId = $methodConfig->getMethod(); + if ($methodId !== $disabledMethodId) { + $method = $this->paymentMethodProvider->getPaymentMethod($methodId); + if ($method->isEnabled()) { + return true; + } + } + } + + return false; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/PaymentMethodIconAwareInterface.php b/src/Marello/Bundle/PaymentBundle/Method/PaymentMethodIconAwareInterface.php new file mode 100644 index 000000000..1b4bc3745 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/PaymentMethodIconAwareInterface.php @@ -0,0 +1,13 @@ +hasMethodView($methodId)) { + return $this; + } + + $this->methodViews[$methodId] = $methodView; + + return $this; + } + + /** + * @param string $methodId + * + * @return bool + */ + public function hasMethodView($methodId) + { + return array_key_exists($methodId, $this->methodViews); + } + + /** + * @param string $methodId + * + * @return $this + */ + public function removeMethodView($methodId) + { + if (false === $this->hasMethodView($methodId)) { + return $this; + } + + unset($this->methodViews[$methodId]); + + return $this; + } + + /** + * @param string $methodId + * + * @return array|null + */ + public function getMethodView($methodId) + { + if (false === $this->hasMethodView($methodId)) { + return null; + } + + return $this->methodViews[$methodId]; + } + + /** + * @return array + */ + public function getAllMethodsViews() + { + return $this->methodViews; + } + + /** + * @return array + */ + public function toArray() + { + $resultingFullMethodViews = []; + + foreach ($this->methodViews as $methodId => $methodView) { + if (false === array_key_exists($methodId, $resultingFullMethodViews)) { + $resultingFullMethodViews[$methodId] = $methodView; + } + } + + uasort( + $resultingFullMethodViews, + function ($methodData1, $methodData2) { + if (false === array_key_exists('sortOrder', $methodData1) + || false === array_key_exists('sortOrder', $methodData2) + ) { + throw new \Exception('Method View should contain sortOrder'); + } + + return $methodData1['sortOrder'] - $methodData2['sortOrder']; + } + ); + + return $resultingFullMethodViews; + } + + /** + * @return self + */ + public function clear() + { + $this->methodViews = []; + + return $this; + } + + /** + * @return bool + */ + public function isEmpty() + { + return count($this->methodViews) <= 0; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/PaymentMethodViewFactory.php b/src/Marello/Bundle/PaymentBundle/Method/PaymentMethodViewFactory.php new file mode 100644 index 000000000..1108662c2 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/PaymentMethodViewFactory.php @@ -0,0 +1,24 @@ + $paymentMethodId, + 'label' => $label, + 'sortOrder' => $sortOrder, + 'options' => $options + ]; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Provider/AbstractPaymentMethodProvider.php b/src/Marello/Bundle/PaymentBundle/Method/Provider/AbstractPaymentMethodProvider.php new file mode 100644 index 000000000..3f20b2b58 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Provider/AbstractPaymentMethodProvider.php @@ -0,0 +1,73 @@ +methods = new ArrayCollection(); + } + + /** + * {@inheritdoc} + */ + public function hasPaymentMethod($identifier) + { + return $this->getMethods()->containsKey($identifier); + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethod($identifier) + { + if (!$this->hasPaymentMethod($identifier)) { + return null; + } + + return $this->getMethods()->get($identifier); + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethods() + { + return $this->getMethods()->toArray(); + } + + /** + * @return ArrayCollection|PaymentMethodInterface[] + */ + protected function getMethods() + { + if ($this->methods->isEmpty()) { + $this->collectMethods(); + } + + return $this->methods; + } + + /** + * @param string $identifier + * @param PaymentMethodInterface $method + */ + protected function addMethod($identifier, PaymentMethodInterface $method) + { + $this->methods->set($identifier, $method); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Provider/CompositePaymentMethodProvider.php b/src/Marello/Bundle/PaymentBundle/Method/Provider/CompositePaymentMethodProvider.php new file mode 100644 index 000000000..e4dab80d6 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Provider/CompositePaymentMethodProvider.php @@ -0,0 +1,60 @@ +providers[] = $provider; + } + + /** + * {@inheritDoc} + */ + public function getPaymentMethods() + { + $result = []; + foreach ($this->providers as $provider) { + $result = array_merge($result, $provider->getPaymentMethods()); + } + + return $result; + } + + /** + * {@inheritDoc} + */ + public function getPaymentMethod($identifier) + { + foreach ($this->providers as $provider) { + if ($provider->hasPaymentMethod($identifier)) { + return $provider->getPaymentMethod($identifier); + } + } + + throw new \InvalidArgumentException('There is no payment method for "' . $identifier . '" identifier'); + } + + /** + * {@inheritDoc} + */ + public function hasPaymentMethod($identifier) + { + foreach ($this->providers as $provider) { + if ($provider->hasPaymentMethod($identifier)) { + return true; + } + } + + return false; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Provider/Integration/ChannelPaymentMethodProvider.php b/src/Marello/Bundle/PaymentBundle/Method/Provider/Integration/ChannelPaymentMethodProvider.php new file mode 100644 index 000000000..12a6eda56 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Provider/Integration/ChannelPaymentMethodProvider.php @@ -0,0 +1,122 @@ +channelType = $channelType; + $this->doctrineHelper = $doctrineHelper; + $this->methodFactory = $methodFactory; + } + + /** + * We need only non dirty channels for creating methods. + * For example if entity was changed on form submit, we will have dirty channel in Unit of work. + * + * @param Channel $channel + * @param LifecycleEventArgs $event + */ + public function postLoad(Channel $channel, LifecycleEventArgs $event) + { + if ($channel->getType() === $this->channelType) { + $this->loadedChannels[] = $channel; + $this->createMethodFromChannel($channel); + } + } + + /** + * {@inheritDoc} + */ + public function getPaymentMethods() + { + $this->loadChannels(); + + return $this->methods; + } + + /** + * {@inheritDoc} + */ + public function getPaymentMethod($name) + { + if ($this->hasPaymentMethod($name)) { + return $this->getPaymentMethods()[$name]; + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function hasPaymentMethod($name) + { + return array_key_exists($name, $this->getPaymentMethods()); + } + + /** + * @param Channel $channel + */ + private function createMethodFromChannel(Channel $channel) + { + $method = $this->methodFactory->create($channel); + $this->methods[$method->getIdentifier()] = $method; + } + + /** + * @return ChannelRepository|\Doctrine\ORM\EntityRepository + */ + private function getRepository() + { + return $this->doctrineHelper->getEntityRepository('OroIntegrationBundle:Channel'); + } + + private function loadChannels() + { + /* After fetching, all entities will be saved into $loadedChannels on postLoad call */ + $this->getRepository()->findByTypeAndExclude($this->channelType, $this->loadedChannels); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProvider.php b/src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProvider.php new file mode 100644 index 000000000..deab3f5a8 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProvider.php @@ -0,0 +1,95 @@ +paymentMethodProvider = $paymentMethodProvider; + $this->paymentMethodsConfigsRulesProvider = $paymentMethodsConfigsRulesProvider; + } + + /** + * @param PaymentContextInterface $context + * + * @return PaymentMethodInterface[] + */ + public function getApplicablePaymentMethods(PaymentContextInterface $context) + { + $paymentMethodsConfigsRules = $this->paymentMethodsConfigsRulesProvider + ->getPaymentMethodsConfigsRules($context); + + $paymentMethods = []; + + foreach ($paymentMethodsConfigsRules as $paymentMethodsConfigsRule) { + $paymentMethods = array_merge( + $paymentMethods, + $this->getPaymentMethodsForConfigsRule($paymentMethodsConfigsRule, $context) + ); + } + + return $paymentMethods; + } + + /** + * @param PaymentMethodsConfigsRule $paymentMethodsConfigsRule + * @param PaymentContextInterface $context + * @return array + */ + protected function getPaymentMethodsForConfigsRule( + PaymentMethodsConfigsRule $paymentMethodsConfigsRule, + PaymentContextInterface $context + ) { + $paymentMethods = []; + foreach ($paymentMethodsConfigsRule->getMethodConfigs() as $methodConfig) { + $paymentMethod = $this->getPaymentMethodForConfig($methodConfig, $context); + if ($paymentMethod) { + $paymentMethods[$methodConfig->getMethod()] = $paymentMethod; + } + } + + return $paymentMethods; + } + + /** + * @param PaymentMethodConfig $methodConfig + * @param PaymentContextInterface $context + * @return PaymentMethodInterface|null + */ + protected function getPaymentMethodForConfig(PaymentMethodConfig $methodConfig, PaymentContextInterface $context) + { + $identifier = $methodConfig->getMethod(); + if ($this->paymentMethodProvider->hasPaymentMethod($identifier)) { + $paymentMethod = $this->paymentMethodProvider->getPaymentMethod($identifier); + + if ($paymentMethod->isApplicable($context)) { + return $paymentMethod; + } + } + + return null; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProviderInterface.php b/src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProviderInterface.php new file mode 100644 index 000000000..8b3f16c7c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Method/Provider/PaymentMethodProviderInterface.php @@ -0,0 +1,25 @@ +setName(self::DEFAULT_RULE_NAME) + ->setEnabled(true) + ->setSortOrder(1); + + $paymentRule = new PaymentMethodsConfigsRule(); + + $paymentRule->setRule($rule) + ->setOrganization($this->getOrganization($manager)) + ->setCurrency('USD'); + + $manager->persist($paymentRule); + $this->addReference(self::DEFAULT_RULE_REFERENCE, $paymentRule); + $manager->flush(); + } + + /** + * @param ObjectManager $manager + * + * @return Organization|object + */ + private function getOrganization(ObjectManager $manager) + { + if ($this->hasReference(LoadOrganizationAndBusinessUnitData::REFERENCE_DEFAULT_ORGANIZATION)) { + return $this->getReference(LoadOrganizationAndBusinessUnitData::REFERENCE_DEFAULT_ORGANIZATION); + } else { + return $manager + ->getRepository('OroOrganizationBundle:Organization') + ->getFirst(); + } + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Migrations/Schema/MarelloPaymentBundleInstaller.php b/src/Marello/Bundle/PaymentBundle/Migrations/Schema/MarelloPaymentBundleInstaller.php new file mode 100644 index 000000000..b82aee0bd --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Migrations/Schema/MarelloPaymentBundleInstaller.php @@ -0,0 +1,203 @@ +createMarelloPaymentMethodConfigTable($schema); + $this->createMarelloPaymentMethodsConfigsRuleTable($schema); + $this->createMarelloPaymentMethodsConfigsRuleDestinationTable($schema); + $this->createMarelloPaymentMethodsConfigsRuleDestinationPostalCodeTable($schema); + + $this->addOroPaymentMethodConfigForeignKeys($schema); + $this->addOroPaymentMethodsConfigsRuleForeignKeys($schema); + $this->addOroPaymentMethodsConfigsRuleDestinationForeignKeys($schema); + $this->addOroPaymentMethodsConfigsRuleDestinationPostalCodeForeignKeys($schema); + } + + /** + * Create marello_payment_method_config table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodConfigTable(Schema $schema) + { + $table = $schema->createTable('marello_payment_method_config'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('configs_rule_id', 'integer', []); + $table->addColumn('method', 'string', ['length' => 255]); + $table->addColumn('options', 'array', ['notnull' => false, 'comment' => '(DC2Type:array)']); + $table->setPrimaryKey(['id']); + } + + /** + * Create marello_payment_mtds_cfgs_rl table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodsConfigsRuleTable(Schema $schema) + { + $table = $schema->createTable('marello_payment_mtds_cfgs_rl'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('rule_id', 'integer', []); + $table->addColumn('currency', 'string', ['notnull' => true, 'length' => 3]); + $table->addColumn('organization_id', 'integer', ['notnull' => false]); + $table->setPrimaryKey(['id']); + + $this->activityExtension->addActivityAssociation( + $schema, + 'oro_note', + 'marello_payment_mtds_cfgs_rl' + ); + } + + /** + * Create marello_payment_mtds_cfgs_rl_d table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodsConfigsRuleDestinationTable(Schema $schema) + { + $table = $schema->createTable('marello_payment_mtds_cfgs_rl_d'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('region_code', 'string', ['notnull' => false, 'length' => 16]); + $table->addColumn('configs_rule_id', 'integer', []); + $table->addColumn('country_code', 'string', ['length' => 2]); + $table->addColumn('region_text', 'string', ['notnull' => false, 'length' => 255]); + $table->setPrimaryKey(['id']); + } + + /** + * Create marello_pmnt_mtdscfgsrl_dst_pc table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodsConfigsRuleDestinationPostalCodeTable(Schema $schema) + { + $table = $schema->createTable('marello_pmnt_mtdscfgsrl_dst_pc'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('destination_id', 'integer', []); + $table->addColumn('name', 'text', []); + $table->setPrimaryKey(['id']); + } + + /** + * Add marello_payment_method_config foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodConfigForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_method_config'); + $table->addForeignKeyConstraint( + $schema->getTable('marello_payment_mtds_cfgs_rl'), + ['configs_rule_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } + + /** + * Add marello_payment_mtds_cfgs_rl foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodsConfigsRuleForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_mtds_cfgs_rl'); + $table->addForeignKeyConstraint( + $schema->getTable('marello_rule'), + ['rule_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_organization'), + ['organization_id'], + ['id'], + ['onDelete' => 'SET NULL', 'onUpdate' => null] + ); + } + + /** + * Add marello_payment_mtds_cfgs_rl_d foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodsConfigsRuleDestinationForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_mtds_cfgs_rl_d'); + $table->addForeignKeyConstraint( + $schema->getTable('oro_dictionary_region'), + ['region_code'], + ['combined_code'], + ['onDelete' => null, 'onUpdate' => null] + ); + $table->addForeignKeyConstraint( + $schema->getTable('marello_payment_mtds_cfgs_rl'), + ['configs_rule_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + $table->addForeignKeyConstraint( + $schema->getTable('oro_dictionary_country'), + ['country_code'], + ['iso2_code'], + ['onDelete' => null, 'onUpdate' => null] + ); + } + + /** + * Add marello_pmnt_mtdscfgsrl_dst_pc foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodsConfigsRuleDestinationPostalCodeForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_pmnt_mtdscfgsrl_dst_pc'); + $table->addForeignKeyConstraint( + $schema->getTable('marello_payment_mtds_cfgs_rl_d'), + ['destination_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } + + /** + * Sets the ActivityExtension + * + * @param ActivityExtension $activityExtension + */ + public function setActivityExtension(ActivityExtension $activityExtension) + { + $this->activityExtension = $activityExtension; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Migrations/Schema/v1_0/MarelloPaymentBundle.php b/src/Marello/Bundle/PaymentBundle/Migrations/Schema/v1_0/MarelloPaymentBundle.php new file mode 100644 index 000000000..4442fe6ad --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Migrations/Schema/v1_0/MarelloPaymentBundle.php @@ -0,0 +1,195 @@ +createMarelloPaymentMethodConfigTable($schema); + $this->createMarelloPaymentMethodsConfigsRuleTable($schema); + $this->createMarelloPaymentMethodsConfigsRuleDestinationTable($schema); + $this->createMarelloPaymentMethodsConfigsRuleDestinationPostalCodeTable($schema); + + $this->addOroPaymentMethodConfigForeignKeys($schema); + $this->addOroPaymentMethodsConfigsRuleForeignKeys($schema); + $this->addOroPaymentMethodsConfigsRuleDestinationForeignKeys($schema); + $this->addOroPaymentMethodsConfigsRuleDestinationPostalCodeForeignKeys($schema); + } + + /** + * Create marello_payment_method_config table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodConfigTable(Schema $schema) + { + $table = $schema->createTable('marello_payment_method_config'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('configs_rule_id', 'integer', []); + $table->addColumn('method', 'string', ['length' => 255]); + $table->addColumn('options', 'array', ['notnull' => false, 'comment' => '(DC2Type:array)']); + $table->setPrimaryKey(['id']); + } + + /** + * Create marello_payment_mtds_cfgs_rl table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodsConfigsRuleTable(Schema $schema) + { + $table = $schema->createTable('marello_payment_mtds_cfgs_rl'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('rule_id', 'integer', []); + $table->addColumn('currency', 'string', ['notnull' => true, 'length' => 3]); + $table->addColumn('organization_id', 'integer', ['notnull' => false]); + $table->setPrimaryKey(['id']); + + $this->activityExtension->addActivityAssociation( + $schema, + 'oro_note', + 'marello_payment_mtds_cfgs_rl' + ); + } + + /** + * Create marello_payment_mtds_cfgs_rl_d table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodsConfigsRuleDestinationTable(Schema $schema) + { + $table = $schema->createTable('marello_payment_mtds_cfgs_rl_d'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('region_code', 'string', ['notnull' => false, 'length' => 16]); + $table->addColumn('configs_rule_id', 'integer', []); + $table->addColumn('country_code', 'string', ['length' => 2]); + $table->addColumn('region_text', 'string', ['notnull' => false, 'length' => 255]); + $table->setPrimaryKey(['id']); + } + + /** + * Create marello_pmnt_mtdscfgsrl_dst_pc table + * + * @param Schema $schema + */ + protected function createMarelloPaymentMethodsConfigsRuleDestinationPostalCodeTable(Schema $schema) + { + $table = $schema->createTable('marello_pmnt_mtdscfgsrl_dst_pc'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('destination_id', 'integer', []); + $table->addColumn('name', 'text', []); + $table->setPrimaryKey(['id']); + } + + /** + * Add marello_payment_method_config foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodConfigForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_method_config'); + $table->addForeignKeyConstraint( + $schema->getTable('marello_payment_mtds_cfgs_rl'), + ['configs_rule_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } + + /** + * Add marello_payment_mtds_cfgs_rl foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodsConfigsRuleForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_mtds_cfgs_rl'); + $table->addForeignKeyConstraint( + $schema->getTable('marello_rule'), + ['rule_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_organization'), + ['organization_id'], + ['id'], + ['onDelete' => 'SET NULL', 'onUpdate' => null] + ); + } + + /** + * Add marello_payment_mtds_cfgs_rl_d foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodsConfigsRuleDestinationForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_mtds_cfgs_rl_d'); + $table->addForeignKeyConstraint( + $schema->getTable('oro_dictionary_region'), + ['region_code'], + ['combined_code'], + ['onDelete' => null, 'onUpdate' => null] + ); + $table->addForeignKeyConstraint( + $schema->getTable('marello_payment_mtds_cfgs_rl'), + ['configs_rule_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + $table->addForeignKeyConstraint( + $schema->getTable('oro_dictionary_country'), + ['country_code'], + ['iso2_code'], + ['onDelete' => null, 'onUpdate' => null] + ); + } + + /** + * Add marello_pmnt_mtdscfgsrl_dst_pc foreign keys. + * + * @param Schema $schema + */ + protected function addOroPaymentMethodsConfigsRuleDestinationPostalCodeForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_pmnt_mtdscfgsrl_dst_pc'); + $table->addForeignKeyConstraint( + $schema->getTable('marello_payment_mtds_cfgs_rl_d'), + ['destination_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } + + /** + * Sets the ActivityExtension + * + * @param ActivityExtension $activityExtension + */ + public function setActivityExtension(ActivityExtension $activityExtension) + { + $this->activityExtension = $activityExtension; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Model/ExtendPaymentMethodConfig.php b/src/Marello/Bundle/PaymentBundle/Model/ExtendPaymentMethodConfig.php new file mode 100644 index 000000000..2cd4fb95c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Model/ExtendPaymentMethodConfig.php @@ -0,0 +1,10 @@ +name; + } + + /** + * @param string $name + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * @param string $description + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * @return float + */ + public function getCost() + { + return $this->cost; + } + + /** + * @param float $cost + * @return $this + */ + public function setCost($cost) + { + $this->cost = (float)$cost; + + return $this; + } + + /** + * @return float + */ + public function getQty() + { + return $this->qty; + } + + /** + * @param float $qty + * @return $this + */ + public function setQty($qty) + { + $this->qty = $qty; + + return $this; + } + + /** + * @return string + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @param string $currency + * @return $this + */ + public function setCurrency($currency) + { + $this->currency = $currency; + + return $this; + } + + /** + * @return string + */ + public function getUnit() + { + return $this->unit; + } + + /** + * @param string $unit + * @return $this + */ + public function setUnit($unit) + { + $this->unit = $unit; + + return $this; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Model/Surcharge.php b/src/Marello/Bundle/PaymentBundle/Model/Surcharge.php new file mode 100644 index 000000000..6e719d70b --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Model/Surcharge.php @@ -0,0 +1,94 @@ +paymentAmount = (float)$amount; + + return $this; + } + + /** + * @return float + */ + public function getPaymentAmount() + { + return $this->paymentAmount; + } + + /** + * @param float $amount + * @return $this + */ + public function setHandlingAmount($amount) + { + $this->handlingAmount = (float)$amount; + + return $this; + } + + /** + * @return float + */ + public function getHandlingAmount() + { + return $this->handlingAmount; + } + + /** + * @param float $amount + * @return $this + */ + public function setDiscountAmount($amount) + { + $this->discountAmount = (float)$amount; + + return $this; + } + + /** + * @return float + */ + public function getDiscountAmount() + { + return $this->discountAmount; + } + + /** + * @param float $amount + * @return $this + */ + public function setInsuranceAmount($amount) + { + $this->insuranceAmount = (float)$amount; + + return $this; + } + + /** + * @return float + */ + public function getInsuranceAmount() + { + return $this->insuranceAmount; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodChoicesProvider.php b/src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodChoicesProvider.php new file mode 100755 index 000000000..444c7b542 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodChoicesProvider.php @@ -0,0 +1,52 @@ +paymentMethodProvider = $paymentMethodProvider; + $this->translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function getMethods($translate = false) + { + return array_reduce( + $this->paymentMethodProvider->getPaymentMethods(), + function (array $result, PaymentMethodInterface $method) use ($translate) { + $label = $method->getLabel(); + if ($translate) { + $label = $this->translator->trans($label); + } + $result[$method->getIdentifier()] = $label; + + return $result; + }, + [] + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodsViewsProvider.php b/src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodsViewsProvider.php new file mode 100644 index 000000000..781c1d7b4 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Provider/BasicPaymentMethodsViewsProvider.php @@ -0,0 +1,81 @@ +paymentRulesProvider = $paymentRulesProvider; + $this->paymentMethodProvider = $paymentMethodProvider; + $this->eventDispatcher = $eventDispatcher; + } + + /** + * @inheritDoc + */ + public function getApplicableMethodsViews(PaymentContextInterface $context) + { + $methodCollection = new PaymentMethodViewCollection(); + + $rules = $this->paymentRulesProvider->getPaymentMethodsConfigsRules($context); + foreach ($rules as $rule) { + foreach ($rule->getMethodConfigs() as $methodConfig) { + $methodId = $methodConfig->getMethod(); + $method = $this->paymentMethodProvider->getPaymentMethod($methodId); + + if (!$method) { + continue; + } + $event = new ApplicablePaymentMethodViewEvent( + $context, + $methodId, + $method->getLabel(), + $methodConfig->getOptions() + ); + $this->eventDispatcher->dispatch(ApplicablePaymentMethodViewEvent::NAME, $event); + $methodView = PaymentMethodViewFactory::createMethodView( + $event->getMethodId(), + $event->getMethodLabel(), + $method->getSortOrder(), + $event->getOptions() + ); + + $methodCollection->addMethodView($methodId, $methodView); + } + } + + return $methodCollection; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Provider/EnabledPaymentMethodChoicesProviderDecorator.php b/src/Marello/Bundle/PaymentBundle/Provider/EnabledPaymentMethodChoicesProviderDecorator.php new file mode 100755 index 000000000..00dbe6b08 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Provider/EnabledPaymentMethodChoicesProviderDecorator.php @@ -0,0 +1,51 @@ +paymentMethodProvider = $paymentMethodProvider; + $this->provider = $provider; + } + + /** + * {@inheritdoc} + */ + public function getMethods($translate = false) + { + $methods = $this->provider->getMethods($translate); + $enabledMethods = []; + foreach ($methods as $methodId => $label) { + $method = $this->paymentMethodProvider->getPaymentMethod($methodId); + if (!$method) { + continue; + } + + if ($method->isEnabled()) { + $enabledMethods[$methodId] = $label; + } + } + + return $enabledMethods; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProvider.php b/src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProvider.php new file mode 100644 index 000000000..709102bef --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProvider.php @@ -0,0 +1,55 @@ +filtrationService = $filtrationService; + $this->repository = $repository; + } + + /** + * {@inheritDoc} + */ + public function getPaymentMethodsConfigsRules(PaymentContextInterface $context) + { + if ($context->getBillingAddress()) { + $methodsConfigsRules = $this->repository->getByDestinationAndCurrency( + $context->getBillingAddress(), + $context->getCurrency() + ); + } else { + $methodsConfigsRules = $this->repository->getByCurrencyWithoutDestination( + $context->getCurrency() + ); + } + + return $this->filtrationService->getFilteredPaymentMethodsConfigsRules( + $methodsConfigsRules, + $context + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/MethodsConfigsRulesByContextProviderInterface.php b/src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/MethodsConfigsRulesByContextProviderInterface.php new file mode 100644 index 000000000..aaf245e78 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Provider/MethodsConfigsRule/Context/MethodsConfigsRulesByContextProviderInterface.php @@ -0,0 +1,15 @@ +filtrationService = $filtrationService; + $this->repository = $repository; + } + + /** + * {@inheritDoc} + */ + public function getPaymentMethodsConfigsRules(PaymentContextInterface $context) + { + if ($context->getBillingAddress()) { + $methodsConfigsRules = $this->repository->getByDestinationAndCurrency( + $context->getBillingAddress(), + $context->getCurrency() + ); + } else { + $methodsConfigsRules = $this->repository->getByCurrency( + $context->getCurrency() + ); + } + + return $this->filtrationService->getFilteredPaymentMethodsConfigsRules( + $methodsConfigsRules, + $context + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Provider/PaymentMethodChoicesProviderInterface.php b/src/Marello/Bundle/PaymentBundle/Provider/PaymentMethodChoicesProviderInterface.php new file mode 100755 index 000000000..6329556a0 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Provider/PaymentMethodChoicesProviderInterface.php @@ -0,0 +1,13 @@ +div { + position: relative; + padding: 15px 15px 0 0; + margin: 0 60% 20px 0; + border: 1px solid #e6e6e6; + background: #f8f8f8; + border-radius: 4px; + min-width: 500px; + } + div[data-validation-optional-group=""] { + .oro-multiselect-holder { + padding-right: 50px !important; + } + } + .add-list-item { + margin-left: 183px; + } + .removeRow { + position: absolute; + width: 28px; + top: 4px; + right: 4px; + text-decoration: none; + &:after { + color: #bbb; + } + } + input[type="text"] { + width: 290px; + } + [data-name*="field__region-text"] { + margin-top:0 !important; + } + input.method-options-surcharge { + width: 110px; + } + .small-row { + .control-group-choice { + float: left; + } + } + .selector.input-widget-select { + margin-left: 0; + } +} + +.marello-payment-rule-add-method-select { + display: inline-block; +} diff --git a/src/Marello/Bundle/PaymentBundle/Resources/public/js/app/views/payment-rule-method-view.js b/src/Marello/Bundle/PaymentBundle/Resources/public/js/app/views/payment-rule-method-view.js new file mode 100644 index 000000000..9bf6f53ca --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/public/js/app/views/payment-rule-method-view.js @@ -0,0 +1,263 @@ +define(function(require) { + 'use strict'; + + const _ = require('underscore'); + const $ = require('jquery'); + const BaseView = require('oroui/js/app/views/base/view'); + const Popover = require('bootstrap-popover'); + const mediator = require('oroui/js/mediator'); + const layout = require('oroui/js/layout'); + + const PaymentRuleMethodsView = BaseView.extend({ + options: { + addSelector: '.add-method', + addAllSelector: '.add-all-methods', + gridSelector: '.payment-methods-grid', + methodSelectSelector: '[name="marello_payment_methods_configs_rule[method]"]', + methodSelector: 'input[data-name="field__method"]', + methodViewSelector: '[data-role="method-view"]', + methodPreviewSelector: '[data-role="method-preview"]', + focusFieldsSelector: 'input:text[name]', + previewFieldsSelector: 'input:text[name],select[name],textarea[name]', + enabledFieldSelector: '[data-name="field__enabled"]', + additionalOptionSelector: '.control-group-marello_payment_method_type_config', + focus: false, + updateFlag: null + }, + + events: { + 'click .add-all-methods': '_onAddAllClick', + 'click .add-method': '_onAddClick', + 'change [name="marello_payment_methods_configs_rule[method]"]': '_onMethodChange', + 'content:remove': '_onMethodRemove', + 'change textarea': '_onInputsChange' + }, + + listen: { + 'page:afterChange mediator': '_onPageAfterChange' + }, + + methods: null, + + $form: null, + + $methodSelect: null, + + $methodSelectClone: null, + + $add: null, + + $addAll: null, + + initialize: function(options) { + this.options = _.defaults(options || {}, this.options); + + this.$form = $(this.el).closest('form'); + this.$methodSelect = this.$(this.options.methodSelectSelector); + this.$methodSelectClone = this.$methodSelect.find('option[value!=""]').clone(); + this.$add = this.$(this.options.addSelector); + this.$addAll = this.$(this.options.addAllSelector); + + this.updateMethodsList(); + this.updateMethodSelect(); + this.updateMethodPreview(); + }, + + _onMethodChange: function() { + this.toggleAddButton(); + }, + + _onAddAllClick: function() { + this.createAddRequest(true); + }, + + _onAddClick: function() { + this.createAddRequest(false); + }, + + _onInputsChange: function(e) { + var $method = $(e.target).closest(this.options.methodViewSelector); + if ($method.length) { + this.updateMethodPreview($method); + } + }, + + _onMethodRemove: function(e) { + var removedMethod = $(e.target).find(this.options.methodSelector).val(); + this.updateMethodsList(removedMethod); + this.updateMethodSelect(); + + if (this.methods.length === 0) { + this.$(this.options.gridSelector).remove(); + } + }, + + _onPageAfterChange: function() { + this.focus(); + }, + + focus: function() { + if (!this.options.focus) { + return; + } + + var focusFieldsSelector = this.options.focusFieldsSelector; + this.$(this.options.methodViewSelector).each(function() { + var $fields = $(this).find(focusFieldsSelector); + var notEmptyFieldsCount = $fields.filter(function() { + return this.value.length > 0; + }).length; + if (notEmptyFieldsCount === 0) { + $fields.eq(0).attr('autofocus', true).focus(); + return false; + } + }); + }, + + toggleAddButton: function() { + this.$add.toggleClass('disabled', _.isEmpty(this.$methodSelect.val())); + }, + + createAddRequest: function(addAll) { + var methodCount = this.methods.length - 1; + var addMethod = function(option) { + methodCount++; + return { + name: 'marello_payment_methods_configs_rule[methodConfigs][' + methodCount + '][method]', + value: option.value + }; + }; + + var data = this.$form.serializeArray(); + if (addAll) { + data.push.apply(data, this.$methodSelect.find('option[value][value!=""]').get().map(addMethod)); + } else { + data.push(addMethod(this.$methodSelect.get(0))); + } + data.push({ + name: this.options.updateFlag, + value: true + }); + + mediator.execute('submitPage', { + url: this.$form.attr('action'), + type: this.$form.attr('method'), + data: $.param(data) + }); + }, + + updateMethodsList: function(removedMethod) { + this.methods = []; + _.each(this.$(this.options.methodSelector), function(option) { + if (!removedMethod || option.value !== removedMethod) { + this.methods.push(option.value); + } + }, this); + }, + + updateMethodSelect: function() { + this.$methodSelect.empty(); + _.each(this.$methodSelectClone, function(option) { + if (_.indexOf(this.methods, option.value) === -1) { + this.$methodSelect.append($(option).clone()); + } + }, this); + + var length = this.$methodSelect.find('option').length; + this.$methodSelect.prop('disabled', length === 0); + this.$methodSelect.inputWidget('refresh'); + this.$methodSelect.inputWidget('val', ''); + + this.$addAll.toggle(length > 1); + this.toggleAddButton(); + }, + + updateMethodPreview: function($method) { + if (_.isUndefined($method)) { + _.each(this.$(this.options.methodViewSelector), function(method) { + this.updateMethodPreview($(method)); + }, this); + return; + } + + var disabled = []; + $method.find(this.options.enabledFieldSelector).each(function() { + if ($(this).is(':checkbox') && !this.checked) { + disabled.push(this.name.replace(/\[enabled\]$/, '')); + } + }); + + var $preview = $method.find(this.options.methodPreviewSelector); + var preview = []; + _.each($method.find(this.options.previewFieldsSelector), function(field) { + var $field = $(field); + var value = _.trim(field.value); + if (value.length === 0) { + return; + } + + var isDisabled = _.filter(disabled, function(name) { + return field.name.indexOf(name) !== -1; + }).length > 0; + if (isDisabled) { + return; + } + + if ($field.is('select')) { + value = $field.find('option:selected').text(); + } + + var $label = this.$('label[for="' + field.id + '"]'); + var label = $label.contents().eq(0).text() + ': '; + + preview.push(label + value); + }, this); + + //replace whitespaces by  , for tooltip overflow calculate + $preview.html(preview.join(', ').replace(/ /g, ' ')); + + this.updatePreviewTooltip($preview, preview); + }, + + updatePreviewTooltip: function($preview, preview) { + if ($preview.data(Popover.DATA_KEY) === void 0) { + layout.initPopoverForElements($preview, { + placement: 'bottom', + trigger: 'hover', + close: false + }, true); + } + + var height = $preview.height(); + $preview.css({ + 'word-wrap': 'break-word', + 'overflow': 'auto', + 'white-space': 'normal' + }); + var isOverflow = $preview.height() > height; + + $preview.attr('style', '').data(Popover.DATA_KEY).updateContent(isOverflow ? preview.join('
') : ''); + }, + + dispose: function() { + if (this.disposed) { + return; + } + + this.$form.off(this.eventNamespace()); + + delete this.methods; + delete this.options; + + delete this.$form; + delete this.$methodSelect; + delete this.$methodSelectClone; + delete this.$add; + delete this.$addAll; + + PaymentRuleMethodsView.__super__.dispose.apply(this, arguments); + } + }); + + return PaymentRuleMethodsView; +}); diff --git a/src/Marello/Bundle/PaymentBundle/Resources/translations/jsmessages.en.yml b/src/Marello/Bundle/PaymentBundle/Resources/translations/jsmessages.en.yml new file mode 100644 index 000000000..29dcb0d51 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/translations/jsmessages.en.yml @@ -0,0 +1,16 @@ +marello: + payment: + validation: + expiration_date: "Invalid Expiration date." + month: "Please, choose correct month." + credit_card: "Invalid card number." + credit_card_type: "Credit card type not accepted." + integration: + deactivate: + message: Some payment methods in the following payment rules depend on the integration that is being disabled. The affected payment methods will be disabled. Payment rule will also be disabled if none of its payment methods remain enabled. + title: Deactivate integration + button.okText: Deactivate + delete: + message: Some payment methods in the following payment rules depend on the integration that is being deleted. The affected payment methods will be disabled. Payment rule will also be disabled if none of its payment methods remain enabled. + title: Delete integration + button.okText: Delete \ No newline at end of file diff --git a/src/Marello/Bundle/PaymentBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/PaymentBundle/Resources/translations/messages.en.yml new file mode 100644 index 000000000..f43599e9f --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/translations/messages.en.yml @@ -0,0 +1,154 @@ +marello: + payment: + menu: + payments.label: Payments + paymentmethodsconfigsrule.description: Payment Rules + paymentmethodsconfigsrule.label: Payment Rules + shortcut_paymentmethodsconfigsrule.description: List of payment rules + datagrid: + action.enable: Enable + action.disable: Disable + status: + success_message: "{0} No entities status were changed|{1} One entity status was changed|]1,Inf[ %count% entities status were changed" + + payment_methods_table: + method.label: Method + options.label: Options + additional_options.label: Additional Options + option.label: Option + status.label: Status + active.label: Active + + methods: + label: "Payment Method" + select_method: 'Select a Payment Method' + no_method: 'No payment methods are available, please contact us to complete the order submission.' + required_field: 'Required Field' + + notification: + channel: + enabled: Payment rule has been enabled successfully + disabled: Payment rule has been disabled successfully + + system_configuration: + groups: + payment.title: 'Payment' + general.title: 'General' + display_options.title: 'Display Options' + merchant_location.title: 'Merchant Location' + fields: + merchant_country.label: 'Merchant Country' + merchant_country.placeholder: 'Please select country...' + enabled.label: 'Enabled' + label.label: 'Checkout Label' + label.tooltip: 'This label is used on the checkout screens' + short_label.label: 'Short Label' + short_label.tooltip: 'This label is used in order history' + sort_order.label: 'Sort Order' + allowed_countries: + label: 'Allowed Countries' + all: 'All' + selected: 'Selected' + selected_countries.label: 'Select Allowed Countries' + + result: + error: Payment failed + error_single_method: 'We were unable to process your payment. Please verify your payment information and try again.' + error_multiple_methods: 'We were unable to process your payment. Please verify your payment information and try again, or try another payment method.' + + security.transaction: Work with payments + capture: Capture + + status: + label: Payment Status + full: Paid in full + authorized: Payment authorized + pending: Pending payment + declined: Payment declined + partially: Paid partially + invoiced: Invoiced + + message: + error: 'General error. We were unable to process action.' + + paymentmethodconfig: + entity_label: Payment Method Config + entity_plural_label: Payment Method Configs + id.label: Id + methods_configs_rule.label: Payment Methods Configs Rule + method.label: Method + options.label: Options + + paymentmethodsconfigsrule: + entity_short_label: Payment Rule + entity_short_plural_label: Payment Rules + entity_label: Payment Rule + entity_plural_label: Payment Rules + id.label: Id + method_configs.label: Payment Methods Configs + rule.label: Rule + destinations.label: Destinations + currency.label: Currency + organization.label: Organization + websites.label: Websites + + notification: + enabled: Payment rule has been enabled successfully + disabled: Payment rule has been disabled successfully + + paymentmethodsconfigsruledestination: + entity_label: Payment Methods Configs Rule Destination + entity_plural_label: Payment Methods Configs Rule Destinations + id.label: Id + methods_configs_rule.label: Payment Methods Configs Rule + postal_codes.label: Postal Codes + region.label: Region + region_text.label: Region Text + country.label: Country + + paymentmethodsconfigsruledestinationpostalcode: + entity_label: Payment Methods Configs Rule Destination Postal Code + entity_plural_label: Payment Methods Configs Rule Destination Postal Codes + id.label: Id + name.label: Name + destination.label: Destination + + paymenttransaction: + entity_plural_label: Payment Transactions + entity_label: Payment Transaction + id.label: Id + paymentMethod.label: Payment Method + action.label: Type + amount.label: Amount + active.label: Active + currency.label: Currency + payment_method.label: Payment Method + reference.label: Reference + frontend_owner.label: Frontend Owner + source_payment_transaction.label: Source Payment Transaction + successful.label: Successful + access_identifier.label: Access Identifier + entity_class.label: Entity Class + entity_identifier.label: Entity Identifier + organization.label: Organization + owner.label: Owner + related_payment_transactions.label: Related Payment Transactions + types: + authorize.label: Authorize + charge.label: Charge + validate.label: Validate + capture.label: Capture + purchase.label: Purchase + invoice.label: Invoice + pending.label: Pending + + sections: + general: 'General Information' + destination: 'Destinations' + paymentrule_configurations: + no_methods.message: 'Please enable at least one payment integration.' + label: 'Payment Method Configurations' + placeholder.label: 'Choose a Method...' + + controller: + rule.saved.message: 'Payment rule has been saved' diff --git a/src/Marello/Bundle/PaymentBundle/Resources/translations/validators.en.yml b/src/Marello/Bundle/PaymentBundle/Resources/translations/validators.en.yml new file mode 100644 index 000000000..4a433c817 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/translations/validators.en.yml @@ -0,0 +1,6 @@ +marello: + payment: + number.error: This value should be a number. + paymentrule: + type.config.count.message: 'You must select at least {{ limit }} payment method type|You must select at least {{ limit }} payment method types' + method.config.count.message: 'You must select at least {{ limit }} payment method|You must select at least {{ limit }} payment methods' diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/Form/fields.html.twig new file mode 100644 index 000000000..8d88d60e7 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/Form/fields.html.twig @@ -0,0 +1,65 @@ +{% block marello_payment_method_config_widget %} + {% import 'MarelloPaymentBundle:PaymentMethodsConfigsRule:macros.html.twig' as PayRuleMacro %} + + {% set collapseView = { + group: 'payment-method', + open: true + } %} + +
+ {% if form.method.vars.value %} +
+
+ + {% set icon = methods_icons[form.method.vars.value]|default('') %} + {% set label = methods_labels[form.method.vars.value]|trans %} + {% if icon %}{{ label }}{% endif %} + {{ label }} + {{ PayRuleMacro.renderPaymentMethodDisabledFlag(form.method.vars.value) }} +
+
+
+ {% endif %} +
+
+ {% set index = (form.method.vars.id|replace({'marello_payment_methods_configs_rule_methodConfigs_': ''})|split('_'))[0] %} + + {{ form_widget(form.options) }} +
+
+
+{% endblock %} + +{% block marello_payment_methods_configs_rule_destination_widget %} + {{ form_row(form.country) }} + {{ form_row(form.region) }} + {{ form_row(form.postalCodes) }} + {{ form_rest(form) }} +{% endblock %} + +{% block marello_payment_method_config_collection_widget %} + {% if form|length != 0 %} +
+
+ {% set collapseView = { + widgetModule: 'oroui/js/widget/collapse-group-widget', + group: 'payment-method' + } %} +
+ + {{ 'marello.payment.payment_methods_table.method.label'|trans }} +
+
{{ 'marello.payment.payment_methods_table.options.label'|trans }}
+
+
+ {{ form_widget(form, {'attr': {'class': 'marello-payment-rule-method-configs-collection row-oro'}}) }} + {{ form_errors(form) }} +
+
+ {% endif %} +{% endblock %} diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/configurations.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/configurations.html.twig new file mode 100644 index 000000000..75f5a4d81 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/configurations.html.twig @@ -0,0 +1,2 @@ +{% import 'MarelloPaymentBundle:PaymentMethodsConfigsRule:macros.html.twig' as PayRuleMacro %} +{{ PayRuleMacro.renderPaymentMethodsConfigs(record.getValue('methodConfigs').toArray(), record.getValue('currency'))}} diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/destinations.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/destinations.html.twig new file mode 100644 index 000000000..e3fec9a0e --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/Datagrid/destinations.html.twig @@ -0,0 +1,10 @@ +{% spaceless %} + {% set destinations = record.getValue('destinations').toArray() %} + + {% set escapedDestinations={} %} + {% for key, destination in destinations %} + {% set escapedDestinations = escapedDestinations|merge({(key): destination|e}) %} + {% endfor %} + + {{ escapedDestinations|join('
')|raw }} +{% endspaceless %} diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/index.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/index.html.twig new file mode 100644 index 000000000..a7c956e62 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/index.html.twig @@ -0,0 +1,17 @@ +{% extends 'OroUIBundle:actions:index.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} +{% set gridName = 'marello-payment-methods-configs-rule-grid' %} +{% set pageTitle = 'marello.payment.paymentmethodsconfigsrule.entity_short_plural_label'|trans %} + +{% block navButtons %} + {% import 'OroUIBundle::macros.html.twig' as UI %} + + {% if is_granted('marello_payment_methods_configs_rule_create') %} +
+ {{ UI.addButton({ + 'path': path('marello_payment_methods_configs_rule_create'), + 'entity_label': 'marello.payment.paymentmethodsconfigsrule.entity_short_label'|trans + }) }} +
+ {% endif %} +{% endblock %} diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/macros.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/macros.html.twig new file mode 100644 index 000000000..ae9c00480 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/macros.html.twig @@ -0,0 +1,24 @@ +{% macro renderPaymentMethodsConfigs(methodConfigs, currency) %} + {%- spaceless -%} +
    + {%- for methodConfig in methodConfigs -%} +
  1. + {%- set paymentMethodData = { + 'identifier': methodConfig.method, + 'options': methodConfig.options, + } -%} + {%- include marello_payment_method_config_template(methodConfig.method) with { + 'currency': currency, + 'methodData': paymentMethodData + } -%} +
  2. + {%- endfor -%} +
+ {%- endspaceless -%} +{% endmacro %} + +{% macro renderPaymentMethodDisabledFlag(identifier) %} + {% if marello_payment_method_enabled(identifier) == false %} + Disabled + {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/paymentMethodWithOptions.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/paymentMethodWithOptions.html.twig new file mode 100644 index 000000000..e15451d76 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/paymentMethodWithOptions.html.twig @@ -0,0 +1,6 @@ +{% spaceless %} + {{ marello_get_payment_method_label(methodData.identifier)|trans }}
+ {% for name, value in methodData.options %} + {{ name ~ ': ' ~ value }}
+ {% endfor %} +{% endspaceless %} diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/update.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/update.html.twig new file mode 100644 index 000000000..3dc814106 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/update.html.twig @@ -0,0 +1,132 @@ +{% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} +{% oro_title_set({params : {"%id%": entity.id|default('N/A'|trans)} }) %} + +{% set formAction = entity.id ? path('marello_payment_methods_configs_rule_update', { 'id': entity.id }) : path('marello_payment_methods_configs_rule_create') %} + +{% block pageHeader %} + {% if entity.id %} + {% set breadcrumbs = { + 'entity': entity, + 'indexPath': path('marello_payment_methods_configs_rule_index'), + 'indexLabel': 'marello.payment.paymentmethodsconfigsrule.entity_short_plural_label'|trans, + 'entityTitle': entity.rule.name|slice(0, 50) + } %} + {{ parent() }} + {% else %} + {% set breadcrumbs = { + 'indexLabel': 'Create', + 'entityTitle': 'Payment Rule', + 'indexPath': path('marello_payment_methods_configs_rule_create') + } %} + {% set title = 'oro.ui.create_entity'|trans({'%entityName%': 'marello.payment.paymentmethodsconfigsrule.entity_short_label'|trans}) %} + {% include 'OroUIBundle::page_title_block.html.twig' with { title: title } %} + {% endif %} +{% endblock pageHeader %} + +{% block navButtons %} + {{ parent() }} + + {{ UI.cancelButton(path('marello_payment_methods_configs_rule_index')) }} + {% if entity.id and is_granted('marello_payment_methods_configs_rule_update') or is_granted('omarello_payment_methods_configs_rule_create') %} + {% set html = '' %} + {% if is_granted('marello_payment_methods_configs_rule_view') %} + {% set html = UI.saveAndCloseButton({ + 'route': 'marello_payment_methods_configs_rule_view', + 'params': {'id': '$id'} + }) %} + {% endif %} + {% set html = html ~ UI.saveAndStayButton({ + 'route': 'marello_payment_methods_configs_rule_update', + 'params': {'id': '$id'} + }) %} + + {{ UI.dropdownSaveButton({'html': html}) }} + {% endif %} +{% endblock navButtons %} + +{% block marello_payment_rule_methods %} + {% set updateFlag = constant('Marello\\Bundle\\ShippingBundle\\Form\\Handler\\ShippingMethodsConfigsRuleHandler::UPDATE_FLAG') %} +
+ {{ block('marello_payment_rule_add_method_widget') }} + + {% if form.method.vars.choices|length == 0 %} +
+ + {{ 'marello.payment.sections.paymentrule_configurations.no_methods.message'|trans }} + +
+ {% endif %} + + {{ form_widget(form.methodConfigs) }} +
+{% endblock %} + +{% block marello_payment_rule_add_method_widget %} + +{% endblock %} + +{% block content_data %} + {% set id = 'payment-methods-configs-rule-edit' %} + + {% set dataBlocks = [ + { + 'title': 'marello.payment.sections.general'|trans, + 'class': 'active', + 'subblocks': [ + { + 'data': [ + form_row(form.rule.enabled), + form_row(form.rule.name), + form_row(form.rule.sortOrder), + form_row(form.currency), + form_row(form.rule.stopProcessing), + ] + }, + ] + }, + { + 'title': 'marello.payment.sections.destination'|trans, + 'subblocks': [ + { + 'data': [ + form_widget(form.destinations, {'attr': {'class': 'marello-payment-rule-collection marello-payment-rule-destinations-collection row-oro'}}), + ] + } + ] + }, + { + 'title': 'marello.payment.sections.paymentrule_configurations.label'|trans, + 'content_attr': { + 'class': 'payment-rule-methods-wrapper' + }, + 'subblocks': [{ + 'data': [ + block('marello_payment_rule_methods') + ] + }] + }] %} + + {% set data = { + 'formErrors': form_errors(form)|nl2br, + 'dataBlocks': dataBlocks + } %} + + {{ parent() }} +{% endblock %} diff --git a/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/view.html.twig b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/view.html.twig new file mode 100644 index 000000000..af8fbab53 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Resources/views/PaymentMethodsConfigsRule/view.html.twig @@ -0,0 +1,69 @@ +{% extends 'OroUIBundle:actions:view.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} +{% import 'OroEntityConfigBundle::macros.html.twig' as entityConfig %} +{% import 'MarelloPaymentBundle:PaymentMethodsConfigsRule:macros.html.twig' as PayRuleMacro %} + +{% oro_title_set({params : {"%label%": entity.rule.name|default('N/A') }}) %} + +{% block pageHeader %} + {% set breadcrumbs = { + 'entity': entity, + 'indexPath': path('marello_payment_methods_configs_rule_index'), + 'indexLabel': 'marello.payment.paymentmethodsconfigsrule.entity_short_plural_label'|trans, + 'entityTitle': entity.rule.name|default('N/A'|trans) + } %} + {{ parent() }} +{% endblock pageHeader %} + +{% block content_data %} + {% import 'OroUIBundle::macros.html.twig' as UI %} + + {%- set paymentRuleInformation -%} +
+
+ {{ UI.renderProperty('marello.rule.name.label'|trans, entity.rule.name) }} + {{ UI.renderProperty('marello.rule.enabled.label'|trans, entity.rule.enabled ? + 'marello.rule.enabled.yes.label'|trans + : + 'marello.rule.enabled.no.label'|trans + ) }} + {{ UI.renderProperty('marello.rule.sort_order.label'|trans, entity.rule.sortOrder) }} + {{ UI.renderProperty('marello.payment.paymentmethodsconfigsrule.currency.label'|trans, entity.currency) }} + + {% if entity.destinations.count %} + {{ UI.renderHtmlProperty( + 'marello.payment.paymentmethodsconfigsrule.destinations.label'|trans, + UI.renderList(entity.destinations)) }} + {% endif %} + + {% if entity.methodConfigs.count %} + {{ UI.renderHtmlProperty( + 'marello.payment.paymentmethodsconfigsrule.method_configs.label'|trans, + PayRuleMacro.renderPaymentMethodsConfigs(entity.methodConfigs, entity.currency)) }} + {% endif %} +
+
+ {{ entityConfig.renderDynamicFields(entity) }} +
+
+ {%- endset -%} + + {% set dataBlocks = [ + { + 'title': 'oro.entity_config.block_titles.general.label'|trans, + 'class': 'active', + 'subblocks': [ + {'data' : [paymentRuleInformation]} + ] + } + ] %} + + {% set id = 'marello-payment-rule-view' %} + {% set data = {'dataBlocks': dataBlocks} %} + + {{ parent() }} +{% endblock content_data %} + +{% block stats %} + {# Must be empty, because parent view uses this block to render dates, which are not implemented in this entity #} +{% endblock stats %} diff --git a/src/Marello/Bundle/PaymentBundle/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationService.php b/src/Marello/Bundle/PaymentBundle/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationService.php new file mode 100644 index 000000000..45504128b --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationService.php @@ -0,0 +1,48 @@ +filtrationService = $filtrationService; + $this->paymentContextToRulesValueConverter = $converter; + } + + /** + * {@inheritDoc} + */ + public function getFilteredPaymentMethodsConfigsRules( + array $paymentMethodsConfigsRules, + PaymentContextInterface $context + ) { + $arrayContext = $this->paymentContextToRulesValueConverter->convert($context); + + return $this->filtrationService->getFilteredRuleOwners( + $paymentMethodsConfigsRules, + $arrayContext + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/RuleFiltration/MethodsConfigsRulesFiltrationServiceInterface.php b/src/Marello/Bundle/PaymentBundle/RuleFiltration/MethodsConfigsRulesFiltrationServiceInterface.php new file mode 100644 index 000000000..662e214bd --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/RuleFiltration/MethodsConfigsRulesFiltrationServiceInterface.php @@ -0,0 +1,20 @@ +initClient(); + $this->client->useHashNavigation(true); + $this->loadFixtures( + [ + LoadPaymentMethodsConfigsRulesWithConfigs::class, + LoadUserData::class + ] + ); + $this->paymentMethodProvider = static::getContainer()->get('marello_payment.payment_method.composite_provider'); + $this->translator = static::getContainer()->get('translator'); + } + + public function testIndexWithoutCreate() + { + $this->initClient([], static::generateBasicAuthHeader(LoadUserData::USER_VIEWER, LoadUserData::USER_VIEWER)); + $crawler = $this->client->request('GET', $this->getUrl('marello_payment_methods_configs_rule_index')); + $result = $this->client->getResponse(); + static::assertHtmlResponseStatusCodeEquals($result, 200); + static::assertEquals(0, $crawler->selectLink('Create Payment Rule')->count()); + } + + /** + * @return string + */ + public function testCreate() + { + $this->initClient( + [], + static::generateBasicAuthHeader(LoadUserData::USER_VIEWER_CREATOR, LoadUserData::USER_VIEWER_CREATOR) + ); + $crawler = $this->client->request('GET', $this->getUrl('marello_payment_methods_configs_rule_create')); + + /** @var Form $form */ + $form = $crawler + ->selectButton('Save and Close') + ->form(); + + $name = 'New Rule'; + + $formValues = $form->getPhpValues(); + $formValues['marello_payment_methods_configs_rule']['rule']['name'] = $name; + $formValues['marello_payment_methods_configs_rule']['rule']['enabled'] = false; + $formValues['marello_payment_methods_configs_rule']['currency'] = 'USD'; + $formValues['marello_payment_methods_configs_rule']['rule']['sortOrder'] = 1; + $formValues['marello_payment_methods_configs_rule']['destinations'] = + [ + [ + 'postalCodes' => '54321', + 'country' => 'FR', + 'region' => 'FR-75' + ] + ]; + $formValues['marello_payment_methods_configs_rule']['methodConfigs'] = + [ + [ + 'method' => $this->getPaymentTermIdentifier(), + 'options' => '', + ] + ]; + + $this->client->followRedirects(true); + $crawler = $this->client->request($form->getMethod(), $form->getUri(), $formValues); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + + $html = $crawler->html(); + + $this->assertContains('Payment rule has been saved', $html); + $this->assertContains('No', $html); + + return $name; + } + + /** + * @depends testCreate + * + * @param string $name + */ + public function testIndex($name) + { + $auth = static::generateBasicAuthHeader(LoadUserData::USER_VIEWER_CREATOR, LoadUserData::USER_VIEWER_CREATOR); + $this->initClient([], $auth); + $crawler = $this->client->request('GET', $this->getUrl('marello_payment_methods_configs_rule_index')); + $result = $this->client->getResponse(); + static::assertHtmlResponseStatusCodeEquals($result, 200); + static::assertContains('marello-payment-methods-configs-rule-grid', $crawler->html()); + $href = $crawler->selectLink('Create Payment Rule')->attr('href'); + static::assertEquals($this->getUrl('marello_payment_methods_configs_rule_create'), $href); + + $response = $this->client->requestGrid( + [ + 'gridName' => 'marello-payment-methods-configs-rule-grid', + 'marello-payment-methods-configs-rule-grid[_sort_by][id]' => 'ASC', + ] + ); + + static::getJsonResponseContent($response, 200); + } + + /** + * @depends testCreate + * + * @param string $name + */ + public function testView($name) + { + $this->initClient([], static::generateBasicAuthHeader()); + $paymentRule = $this->getPaymentMethodsConfigsRuleByName($name); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_payment_methods_configs_rule_view', ['id' => $paymentRule->getId()]) + ); + + $result = $this->client->getResponse(); + static::assertHtmlResponseStatusCodeEquals($result, 200); + + $html = $crawler->html(); + + $this->assertContains($paymentRule->getRule()->getName(), $html); + $destination = $paymentRule->getDestinations(); + $this->assertContains((string)$destination[0], $html); + $methodConfigs = $paymentRule->getMethodConfigs(); + $label = $this->paymentMethodProvider + ->getPaymentMethod($methodConfigs[0]->getMethod()) + ->getLabel(); + $this->assertContains($this->translator->trans($label), $html); + } + + /** + * @depends testCreate + * + * @param string $name + * + * @return PaymentMethodsConfigsRule|object|null + */ + public function testUpdate($name) + { + $paymentRule = $this->getPaymentMethodsConfigsRuleByName($name); + + $this->assertNotEmpty($paymentRule); + + $id = $paymentRule->getId(); + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_payment_methods_configs_rule_update', ['id' => $id]) + ); + + /** @var Form $form */ + $form = $crawler->selectButton('Save and Close')->form(); + + $newName = 'New name for new rule'; + $formValues = $form->getPhpValues(); + $formValues['marello_payment_methods_configs_rule']['rule']['name'] = $newName; + $formValues['marello_payment_methods_configs_rule']['rule']['enabled'] = false; + $formValues['marello_payment_methods_configs_rule']['currency'] = 'USD'; + $formValues['marello_payment_methods_configs_rule']['rule']['sortOrder'] = 1; + $formValues['marello_payment_methods_configs_rule']['destinations'] = + [ + [ + 'postalCodes' => '54321', + 'country' => 'TH', + 'region' => 'TH-83' + ] + ]; + $formValues['marello_payment_methods_configs_rule']['methodConfigs'] = + [ + [ + 'method' => $this->getPaymentTermIdentifier(), + 'options' => '', + ] + ]; + + $this->client->followRedirects(true); + $crawler = $this->client->request($form->getMethod(), $form->getUri(), $formValues); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + $html = $crawler->html(); + static::assertContains('Payment rule has been saved', $html); + + $paymentRule = $this->getPaymentMethodsConfigsRuleByName($newName); + static::assertEquals($id, $paymentRule->getId()); + + $destination = $paymentRule->getDestinations(); + static::assertEquals('TH', $destination[0]->getCountry()->getIso2Code()); + static::assertEquals('TH-83', $destination[0]->getRegion()->getCombinedCode()); + static::assertEquals('54321', $destination[0]->getPostalCodes()->current()->getName()); + $methodConfigs = $paymentRule->getMethodConfigs(); + static::assertEquals($this->getPaymentTermIdentifier(), $methodConfigs[0]->getMethod()); + static::assertFalse($paymentRule->getRule()->isEnabled()); + + return $paymentRule; + } + + /** + * @depends testUpdate + * + * @param PaymentMethodsConfigsRule $paymentRule + */ + public function testCancel(PaymentMethodsConfigsRule $paymentRule) + { + $paymentRule = $this->getPaymentMethodsConfigsRuleByName($paymentRule->getRule()->getName()); + + $this->assertNotEmpty($paymentRule); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_payment_methods_configs_rule_update', ['id' => $paymentRule->getId()]) + ); + + $link = $crawler->selectLink('Cancel')->link(); + $this->client->click($link); + $response = $this->client->getResponse(); + + static::assertHtmlResponseStatusCodeEquals($response, 200); + + $html = $response->getContent(); + + static::assertContains($paymentRule->getRule()->getName(), $html); + $destination = $paymentRule->getDestinations(); + static::assertContains((string)$destination[0], $html); + $methodConfigs = $paymentRule->getMethodConfigs(); + $label = $this->paymentMethodProvider + ->getPaymentMethod($methodConfigs[0]->getMethod()) + ->getLabel(); + static::assertContains($this->translator->trans($label), $html); + } + + /** + * @depends testUpdate + * + * @param PaymentMethodsConfigsRule $paymentRule + * + * @return PaymentMethodsConfigsRule + */ + public function testUpdateRemoveDestination(PaymentMethodsConfigsRule $paymentRule) + { + $this->assertNotEmpty($paymentRule); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_payment_methods_configs_rule_update', ['id' => $paymentRule->getId()]) + ); + + /** @var Form $form */ + $form = $crawler->selectButton('Save and Close')->form(); + + $formValues = $form->getPhpValues(); + $formValues['marello_payment_methods_configs_rule']['destinations'] = []; + + $this->client->followRedirects(true); + $this->client->request($form->getMethod(), $form->getUri(), $formValues); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + $paymentRule = $this->getEntityManager()->find( + 'MarelloPaymentBundle:PaymentMethodsConfigsRule', + $paymentRule->getId() + ); + static::assertCount(0, $paymentRule->getDestinations()); + + return $paymentRule; + } + + public function testStatusDisableMass() + { + $this->initClient([], static::generateBasicAuthHeader()); + /** @var PaymentMethodsConfigsRule $paymentRule1 */ + $paymentRule1 = $this->getReference('payment_rule.1'); + /** @var PaymentMethodsConfigsRule $paymentRule2 */ + $paymentRule2 = $this->getReference('payment_rule.2'); + $url = $this->getUrl( + 'marello_payment_methods_configs_massaction', + [ + 'gridName' => 'marello-payment-methods-configs-rule-grid', + 'actionName' => 'disable', + 'inset' => 1, + 'values' => sprintf( + '%s,%s', + $paymentRule1->getId(), + $paymentRule2->getId() + ) + ] + ); + $this->ajaxRequest('GET', $url); + $result = $this->client->getResponse(); + $data = json_decode($result->getContent(), true); + $this->assertTrue($data['successful']); + $this->assertSame(2, $data['count']); + $this->assertFalse( + $this + ->getPaymentMethodsConfigsRuleById($paymentRule1->getId()) + ->getRule() + ->isEnabled() + ); + $this->assertFalse( + $this + ->getPaymentMethodsConfigsRuleById($paymentRule2->getId()) + ->getRule() + ->isEnabled() + ); + } + + /** + * @depends testStatusDisableMass + */ + public function testStatusEnableMass() + { + $this->initClient([], static::generateBasicAuthHeader()); + /** @var PaymentMethodsConfigsRule $paymentRule1 */ + $paymentRule1 = $this->getReference('payment_rule.1'); + /** @var PaymentMethodsConfigsRule $paymentRule2 */ + $paymentRule2 = $this->getReference('payment_rule.2'); + $url = $this->getUrl( + 'marello_payment_methods_configs_massaction', + [ + 'gridName' => 'marello-payment-methods-configs-rule-grid', + 'actionName' => 'enable', + 'inset' => 1, + 'values' => sprintf( + '%s,%s', + $paymentRule1->getId(), + $paymentRule2->getId() + ) + ] + ); + $this->ajaxRequest('GET', $url); + $result = $this->client->getResponse(); + $data = json_decode($result->getContent(), true); + $this->assertTrue($data['successful']); + $this->assertSame(2, $data['count']); + $this->assertTrue( + $this + ->getPaymentMethodsConfigsRuleById($paymentRule1->getId()) + ->getRule() + ->isEnabled() + ); + $this->assertTrue( + $this + ->getPaymentMethodsConfigsRuleById($paymentRule2->getId()) + ->getRule() + ->isEnabled() + ); + } + + public function testPaymentMethodsConfigsRuleEditWOPermission() + { + $authParams = static::generateBasicAuthHeader(LoadUserData::USER_VIEWER, LoadUserData::USER_VIEWER); + $this->initClient([], $authParams); + + /** @var PaymentMethodsConfigsRule $paymentRule */ + $paymentRule = $this->getReference('payment_rule.1'); + + $this->client->request( + 'GET', + $this->getUrl('marello_payment_methods_configs_rule_update', ['id' => $paymentRule->getId()]) + ); + + static::assertJsonResponseStatusCodeEquals($this->client->getResponse(), 403); + } + + public function testPaymentMethodsConfigsRuleEdit() + { + $authParams = static::generateBasicAuthHeader(LoadUserData::USER_EDITOR, LoadUserData::USER_EDITOR); + $this->initClient([], $authParams); + + /** @var PaymentMethodsConfigsRule $paymentRule */ + $paymentRule = $this->getReference('payment_rule.1'); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_payment_methods_configs_rule_update', ['id' => $paymentRule->getId()]) + ); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + + /** @var Form $form */ + $form = $crawler->selectButton('Save')->form(); + + $rule = $paymentRule->getRule(); + $form['marello_payment_methods_configs_rule[rule][enabled]'] = !$rule->isEnabled(); + $form['marello_payment_methods_configs_rule[rule][name]'] = $rule->getName() . ' new name'; + $form['marello_payment_methods_configs_rule[rule][sortOrder]'] = $rule->getSortOrder() + 1; + $form['marello_payment_methods_configs_rule[currency]'] = $paymentRule->getCurrency() === 'USD' ? 'EUR' : 'USD'; + $form['marello_payment_methods_configs_rule[rule][stopProcessing]'] = !$rule->isStopProcessing(); + $form['marello_payment_methods_configs_rule[destinations][0][postalCodes]'] = '11111'; + + $this->client->followRedirects(true); + $crawler = $this->client->submit($form); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + static::assertContains('Payment rule has been saved', $crawler->html()); + } + + public function testDeleteButtonNotVisible() + { + $authParams = static::generateBasicAuthHeader(LoadUserData::USER_VIEWER, LoadUserData::USER_VIEWER); + $this->initClient([], $authParams); + + $response = $this->client->requestGrid( + ['gridName' => 'marello-payment-methods-configs-rule-grid'], + [], + true + ); + + $result = static::getJsonResponseContent($response, 200); + + $this->assertEquals(false, isset($result['metadata']['massActions']['delete'])); + } + + /** + * @return ObjectManager|null + */ + protected function getEntityManager() + { + return static::getContainer() + ->get('doctrine') + ->getManagerForClass('MarelloPaymentBundle:PaymentMethodsConfigsRule'); + } + + /** + * @param string $name + * + * @return PaymentMethodsConfigsRule|null + */ + protected function getPaymentMethodsConfigsRuleByName($name) + { + /** @var RuleInterface $rule */ + $rule = $this + ->getEntityManager() + ->getRepository('MarelloRuleBundle:Rule') + ->findOneBy(['name' => $name]); + + return $this + ->getEntityManager() + ->getRepository('MarelloPaymentBundle:PaymentMethodsConfigsRule') + ->findOneBy(['rule' => $rule]); + } + + /** + * @param int $id + * + * @return PaymentMethodsConfigsRule|null + */ + protected function getPaymentMethodsConfigsRuleById($id) + { + return $this->getEntityManager() + ->getRepository('MarelloPaymentBundle:PaymentMethodsConfigsRule') + ->find($id); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodConfigsWithFakeMethods.php b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodConfigsWithFakeMethods.php new file mode 100644 index 000000000..1db26c407 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodConfigsWithFakeMethods.php @@ -0,0 +1,83 @@ +getPaymentMethodConfigsData() as $reference => $data) { + $this->loadPaymentMethodConfig($reference, $data, $manager); + } + + $manager->flush(); + } + + /** + * @return array + */ + protected function getPaymentMethodConfigsData() + { + return Yaml::parse(file_get_contents(__DIR__.'/data/payment_method_configs_with_fake_methods.yml')); + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + */ + private function loadPaymentMethodConfig($reference, $data, ObjectManager $manager) + { + $methodsConfigsRule = $this->getPaymentMethodsConfigsRule($data['methods_configs_rule']); + + $methodConfig = $this->createMethodConfig($methodsConfigsRule, $data['method']); + + $manager->persist($methodConfig); + + $this->setReference($reference, $methodConfig); + } + + /** + * @param PaymentMethodsConfigsRule $configsRule + * @param string $method + * + * @return PaymentMethodConfig + */ + private function createMethodConfig(PaymentMethodsConfigsRule $configsRule, $method) + { + $methodConfig = new PaymentMethodConfig(); + + return $methodConfig->setMethodsConfigsRule($configsRule) + ->setMethod($method); + } + + /** + * @param string $reference + * + * @return PaymentMethodsConfigsRule|object + */ + private function getPaymentMethodsConfigsRule($reference) + { + return $this->getReference($reference); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRules.php b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRules.php new file mode 100644 index 000000000..75b39894b --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRules.php @@ -0,0 +1,79 @@ +getPaymentMethodsConfigsRulesData() as $reference => $data) { + $this->loadPaymentMethodsConfigsRule($reference, $data, $manager); + } + + $manager->flush(); + } + + /** + * @return array + */ + protected function getPaymentMethodsConfigsRulesData() + { + return Yaml::parse(file_get_contents(__DIR__.'/data/payment_methods_configs_rules.yml')); + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + */ + private function loadPaymentMethodsConfigsRule($reference, $data, ObjectManager $manager) + { + $rule = $this->buildRule($reference, $data['rule']); + + $configRule = $this->createMethodsConfigsRule($rule, $data['currency']); + + $manager->persist($configRule); + + $this->setReference($reference, $configRule); + } + + /** + * @param RuleInterface $rule + * @param $currency + * + * @return PaymentMethodsConfigsRule + */ + private function createMethodsConfigsRule(RuleInterface $rule, $currency) + { + $configRule = new PaymentMethodsConfigsRule(); + + return $configRule->setRule($rule) + ->setCurrency($currency); + } + + /** + * @param string $reference + * @param array $ruleData + * + * @return RuleInterface + */ + private function buildRule($reference, $ruleData) + { + $rule = new Rule(); + + return $rule->setName($reference) + ->setEnabled($ruleData['enabled']) + ->setSortOrder($ruleData['sortOrder']) + ->setExpression($ruleData['expression']); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRulesWithConfigs.php b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRulesWithConfigs.php new file mode 100644 index 000000000..d15b56083 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadPaymentMethodsConfigsRulesWithConfigs.php @@ -0,0 +1,206 @@ +getPaymentRuleData() as $reference => $data) { + $this->loadPaymentRule($reference, $data, $manager); + } + + $manager->flush(); + } + + /** + * @return array + */ + protected function getPaymentRuleData(): array + { + return Yaml::parse(file_get_contents(__DIR__.'/data/payment_methods_configs_rules_with_configs.yml')); + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + */ + private function loadPaymentRule($reference, $data, ObjectManager $manager) + { + $rule = $this->buildRule($reference, $data); + $configRule = $this->buildMethodsConfigsRule($reference, $data, $rule, $manager); + + $this->setReference($reference, $configRule); + + $manager->persist($configRule); + } + + /** + * @param string $reference + * @param array $data + * @param RuleInterface $rule + * @param ObjectManager $manager + * + * @return PaymentMethodsConfigsRule + */ + protected function buildMethodsConfigsRule( + string $reference, + array $data, + RuleInterface $rule, + ObjectManager $manager + ) { + $configRule = new PaymentMethodsConfigsRule(); + + $configRule + ->setRule($rule) + ->setCurrency($data['currency']) + ->setOrganization($this->getOrganization()); + + $this->setDestinations($configRule, $manager, $data); + $this->setMethodConfigs($configRule, $manager, $data); + + return $configRule; + } + + /** + * @param string $reference + * @param array $data + * + * @return Rule + */ + protected function buildRule(string $reference, array $data) + { + $rule = new Rule(); + + $rule->setName($reference) + ->setEnabled($data['rule']['enabled']) + ->setSortOrder($data['rule']['sortOrder']) + ->setExpression($data['rule']['expression']); + + return $rule; + } + + /** + * @param PaymentMethodsConfigsRule $configRule + * @param ObjectManager $manager + * @param array $data + */ + private function setDestinations(PaymentMethodsConfigsRule $configRule, ObjectManager $manager, $data) + { + if (!array_key_exists('destinations', $data)) { + return; + } + + foreach ($data['destinations'] as $destination) { + /** @var Country $country */ + $country = $manager + ->getRepository('OroAddressBundle:Country') + ->findOneBy(['iso2Code' => $destination['country']]); + + $paymentRuleDestination = new PaymentMethodsConfigsRuleDestination(); + $paymentRuleDestination + ->setMethodsConfigsRule($configRule) + ->setCountry($country); + + if (array_key_exists('region', $destination)) { + /** @var Region $region */ + $region = $manager + ->getRepository('OroAddressBundle:Region') + ->findOneBy(['combinedCode' => $destination['country'].'-'.$destination['region']]); + $paymentRuleDestination->setRegion($region); + } + + if (array_key_exists('postalCodes', $destination)) { + foreach ($destination['postalCodes'] as $postalCode) { + $destinationPostalCode = new PaymentMethodsConfigsRuleDestinationPostalCode(); + $destinationPostalCode->setName($postalCode['name']) + ->setDestination($paymentRuleDestination); + + $paymentRuleDestination->addPostalCode($destinationPostalCode); + } + } + + $manager->persist($paymentRuleDestination); + $configRule->addDestination($paymentRuleDestination); + } + } + + /** + * @param PaymentMethodsConfigsRule $configRule + * @param ObjectManager $manager + * @param array $data + */ + private function setMethodConfigs(PaymentMethodsConfigsRule $configRule, ObjectManager $manager, $data) + { + if (!array_key_exists('methodConfigs', $data)) { + return; + } + + $methodConfig = $this->buildMethodConfig($configRule); + $configRule->addMethodConfig($methodConfig); + + $manager->persist($methodConfig); + } + + /** + * @param PaymentMethodsConfigsRule $configRule + * + * @return PaymentMethodConfig + */ + private function buildMethodConfig(PaymentMethodsConfigsRule $configRule) + { + $methodConfig = new PaymentMethodConfig(); + + $methodConfig + ->setMethodsConfigsRule($configRule) + ->setMethod($this->getPaymentTermIdentifier()); + + return $methodConfig; + } + + /** + * @return Organization + */ + private function getOrganization() + { + return $this->container->get('doctrine') + ->getRepository('OroOrganizationBundle:Organization') + ->getFirst(); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadUserData.php b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadUserData.php new file mode 100644 index 000000000..1c3d14c35 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/LoadUserData.php @@ -0,0 +1,113 @@ + [ + [ + 'class' => PaymentMethodsConfigsRule::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'VIEW', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + [ + 'class' => Channel::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'VIEW', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + ], + self::ROLE_EDIT => [ + [ + 'class' => PaymentMethodsConfigsRule::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'EDIT', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + ], + self::ROLE_CREATE => [ + [ + 'class' => PaymentMethodsConfigsRule::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'CREATE', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + ], + ]; + + /** + * @var array + */ + protected $users = [ + [ + 'email' => 'payment-user-viewer@example.com', + 'username' => self::USER_VIEWER, + 'password' => self::USER_VIEWER, + 'firstname' => 'PaymentUser1FN', + 'lastname' => 'PaymentUser1LN', + 'roles' => [self::ROLE_VIEW], + ], + [ + 'email' => 'payment-user-editor@example.com', + 'username' => self::USER_EDITOR, + 'password' => self::USER_EDITOR, + 'firstname' => 'PaymentUser2FN', + 'lastname' => 'PaymentUser2LN', + 'roles' => [self::ROLE_VIEW, self::ROLE_EDIT], + ], + [ + 'email' => 'payment-user-viewer-creator@example.com', + 'username' => self::USER_VIEWER_CREATOR, + 'password' => self::USER_VIEWER_CREATOR, + 'firstname' => 'PaymentUser2FN', + 'lastname' => 'PaymentUser2LN', + 'roles' => [self::ROLE_VIEW, self::ROLE_CREATE], + ] + ]; + + /** + * {@inheritdoc} + */ + protected function getRolesData() + { + return $this->roles; + } + + /** + * {@inheritdoc} + */ + protected function getUsersData() + { + return $this->users; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_method_configs_with_fake_methods.yml b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_method_configs_with_fake_methods.yml new file mode 100644 index 000000000..e6dc6fc28 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_method_configs_with_fake_methods.yml @@ -0,0 +1,15 @@ +payment_rule.1.method_config.1: + methods_configs_rule: payment_rule.1 + method: ups + +payment_rule.2.method_config.1: + methods_configs_rule: payment_rule.2 + method: flat_rate + +payment_rule.3.method_config.1: + methods_configs_rule: payment_rule.3 + method: dpd + +payment_rule.3.method_config_without_type_configs: + methods_configs_rule: payment_rule.3 + method: ups diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules.yml b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules.yml new file mode 100644 index 000000000..778939764 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules.yml @@ -0,0 +1,27 @@ +payment_rule.1: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'EUR' + +payment_rule.2: + rule: + enabled: true + sortOrder: 1 + expression: 'true' + currency: 'EUR' + +payment_rule.3: + rule: + enabled: false + sortOrder: 2 + expression: 'true' + currency: 'EUR' + +payment_rule.4: + rule: + enabled: true + sortOrder: 3 + expression: 'true' + currency: 'EUR' diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules_with_configs.yml b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules_with_configs.yml new file mode 100644 index 000000000..64b6d3be0 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/DataFixtures/data/payment_methods_configs_rules_with_configs.yml @@ -0,0 +1,205 @@ +payment_rule.1: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_order' + price: 10 + destinations: + - + postalCodes: + - + name: '12345' + country: 'US' + region: 'NY' +payment_rule.2: + rule: + enabled: true + sortOrder: 1 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + country: 'US' + - + country: 'FR' + region: 'FR-75' +payment_rule.3: + rule: + enabled: false + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + country: 'US' + region: 'NY' + - + country: 'FR' +payment_rule.4: + rule: + enabled: true + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + postalCodes: + - + name: '12345' + - + name: '12346' + country: 'US' + region: 'NY' +payment_rule.5: + rule: + enabled: true + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 +payment_rule.6: + rule: + enabled: true + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + country: 'FR' +payment_rule.7: + rule: + enabled: true + sortOrder: 3 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + postalCodes: + - + name: '12346' + country: 'US' + region: 'NY' +payment_rule.8: + rule: + enabled: true + sortOrder: 3 + expression: 'true' + currency: 'EUR' + destinations: + - + country: 'US' + region: 'AL' +payment_rule.9: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'USD' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_order' + price: 10 +payment_rule.10: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'UAH' +payment_rule.11: + rule: + enabled: false + sortOrder: 0 + expression: 'true' + currency: 'UAH' +payment_rule.12: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'UAH' + destinations: + - + country: 'US' + region: 'NY' +payment_rule_without_type_configs: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'USD' + methodConfigs: + - + typeConfigs: [] +payment_rule_with_disabled_type_configs: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'USD' + methodConfigs: + - + typeConfigs: + - + enabled: false + options: + type: 'per_order' + price: 10 diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodConfigRepositoryTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodConfigRepositoryTest.php new file mode 100644 index 000000000..11ad631e0 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodConfigRepositoryTest.php @@ -0,0 +1,51 @@ +initClient([], static::generateBasicAuthHeader()); + $this->loadFixtures([ + LoadPaymentMethodConfigsWithFakeMethods::class, + ]); + + $this->repository = static::getContainer()->get('doctrine') + ->getRepository('MarelloPaymentBundle:PaymentMethodConfig'); + } + + public function testDeleteByMethod() + { + $method = 'ups'; + + static::assertNotEmpty($this->repository->findByMethod($method)); + + $this->repository->deleteByMethod($method); + + static::assertEmpty($this->repository->findByMethod($method)); + } + + public function testDeleteMethodConfigByIds() + { + $ids = [ + $this->getReference('payment_rule.3.method_config_without_type_configs')->getId(), + ]; + + $this->repository->deleteByIds($ids); + + static::assertEmpty($this->repository->findBy(['id' => $ids])); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodsConfigsRuleRepositoryTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodsConfigsRuleRepositoryTest.php new file mode 100644 index 000000000..cf1a50636 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/Entity/Repository/PaymentMethodsConfigsRuleRepositoryTest.php @@ -0,0 +1,248 @@ +initClient([], static::generateBasicAuthHeader()); + $this->client->useHashNavigation(true); + + $this->loadFixtures([ + LoadPaymentMethodsConfigsRulesWithConfigs::class, + ]); + + $this->em = static::getContainer()->get('doctrine') + ->getManagerForClass('MarelloPaymentBundle:PaymentMethodsConfigsRule'); + $this->repository = $this->em->getRepository('MarelloPaymentBundle:PaymentMethodsConfigsRule'); + } + + /** + * @param array $entities + * + * @return array + */ + private function getEntitiesIds(array $entities) + { + return array_map(function ($entity) { + return $entity->getId(); + }, $entities); + } + + /** + * @dataProvider getByDestinationAndCurrencyDataProvider + * + * @param array $shippingAddressData + * @param string $currency + * @param PaymentMethodsConfigsRule[] $expectedRules + */ + public function testGetByDestinationAndCurrency(array $shippingAddressData, $currency, array $expectedRules) + { + $expectedRulesIds = $this->getEntitiesIds($this->getEntitiesByReferences($expectedRules)); + $actualRules = $this->repository->getByDestinationAndCurrency( + $this->createShippingAddress($shippingAddressData), + $currency + ); + + $this->assertEquals($expectedRulesIds, $this->getEntitiesIds($actualRules)); + } + + /** + * @return array + */ + public function getByDestinationAndCurrencyDataProvider() + { + return [ + [ + 'shippingAddress' => [ + 'country' => 'US', + 'region' => [ + 'combinedCode' => 'US-NY', + 'code' => 'NY', + ], + 'postalCode' => '12345', + ], + 'currency' => 'EUR', + 'expectedRulesIds' => [ + 'payment_rule.1', + 'payment_rule.2', + 'payment_rule.3', + 'payment_rule.4', + 'payment_rule.5', + ] + ], + ]; + } + + public function testGetByCurrencyWithoutDestination() + { + $currency = 'UAH'; + $expectedRules = $this->getEntitiesByReferences([ + 'payment_rule.10', + 'payment_rule.11' + ]); + + $actualRules = $this->repository->getByCurrencyWithoutDestination($currency); + + $this->assertEquals($this->getEntitiesIds($expectedRules), $this->getEntitiesIds($actualRules)); + } + + public function testGetRulesWithoutPaymentMethods() + { + $rulesWithoutPaymentMethods = $this->repository->getRulesWithoutPaymentMethods(); + $enabledRulesWithoutPaymentMethods = $this->repository->getRulesWithoutPaymentMethods(true); + + static::assertCount(4, $rulesWithoutPaymentMethods); + static::assertCount(3, $enabledRulesWithoutPaymentMethods); + } + + public function testDisableRulesWithoutPaymentMethods() + { + $this->repository->disableRulesWithoutPaymentMethods(); + + $rulesWithoutPaymentMethods = $this->repository->getRulesWithoutPaymentMethods(); + $enabledRulesWithoutPaymentMethods = $this->repository->getRulesWithoutPaymentMethods(true); + + static::assertCount(4, $rulesWithoutPaymentMethods); + static::assertCount(0, $enabledRulesWithoutPaymentMethods); + } + + public function testGetRulesByMethod() + { + $rulesByExistingMethod = $this->repository->getRulesByMethod($this->getPaymentTermIdentifier()); + + $expectedRuleReferences = [ + 'payment_rule.1', + 'payment_rule.2', + 'payment_rule.3', + 'payment_rule.4', + 'payment_rule.5', + 'payment_rule.6', + 'payment_rule.7', + 'payment_rule.9', + 'payment_rule_without_type_configs', + 'payment_rule_with_disabled_type_configs', + ]; + foreach ($expectedRuleReferences as $expectedRuleReference) { + static::assertContains($this->getReference($expectedRuleReference), $rulesByExistingMethod); + } + + $rulesByNotExistingMethod = $this->repository->getRulesByMethod('not_existing_method'); + static::assertCount(0, $rulesByNotExistingMethod); + } + + /** + * @dataProvider getEnabledRulesByMethodDataProvider + * + * @param string[] $expectedRuleReferences + */ + public function testGetEnabledRulesByMethod(array $expectedRuleReferences) + { + $actualRules = $this->repository->getEnabledRulesByMethod($this->getPaymentTermIdentifier()); + + foreach ($expectedRuleReferences as $expectedRuleReference) { + static::assertContains($this->getReference($expectedRuleReference), $actualRules); + } + } + + /** + * @return array + */ + public function getEnabledRulesByMethodDataProvider() + { + return [ + [ + 'expectedRuleReferences' => [ + 'payment_rule.1', + 'payment_rule.2', + 'payment_rule.4', + 'payment_rule.5', + 'payment_rule.6', + 'payment_rule.7', + 'payment_rule.9', + 'payment_rule_without_type_configs', + 'payment_rule_with_disabled_type_configs', + ] + ] + ]; + } + + /** + * @param array $rules + * + * @return array + */ + protected function getEntitiesByReferences(array $rules) + { + return array_map(function ($ruleReference) { + return $this->getReference($ruleReference); + }, $rules); + } + + /** + * @param array $data + * + * @return AddressInterface|object + */ + protected function createShippingAddress(array $data) + { + return $this->getEntity(ShippingAddressStub::class, [ + 'country' => new Country($data['country']), + 'region' => $this->getEntity( + Region::class, + [ + 'code' => $data['region']['code'], + ], + [ + 'combinedCode' => $data['region']['combinedCode'], + ] + ), + 'postalCode' => $data['postalCode'], + ]); + } + + public function testGetByCurrency() + { + $expectedRules = $this->getEntitiesByReferences([ + 'payment_rule.10', + 'payment_rule.11', + 'payment_rule.12' + ]); + + $this->assertEquals( + $this->getEntitiesIds($expectedRules), + $this->getEntitiesIds($this->repository->getByCurrency('UAH')) + ); + } + + public function testGetByCurrencyWhenCurrencyNotExists() + { + $this->assertEmpty($this->repository->getByCurrency('WON')); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Functional/Helper/PaymentTermIntegrationTrait.php b/src/Marello/Bundle/PaymentBundle/Tests/Functional/Helper/PaymentTermIntegrationTrait.php new file mode 100644 index 000000000..56986a17c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Functional/Helper/PaymentTermIntegrationTrait.php @@ -0,0 +1,34 @@ +getChannelReference(); + + return sprintf('payment_term_%s', $channel->getId()); + } + + /** + * @return Channel + */ + protected function getChannelReference() + { + return $this->getReference(LoadPaymentTermIntegration::REFERENCE_PAYMENT_TERM); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentMethodEnabledByIdentifierCheckerTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentMethodEnabledByIdentifierCheckerTest.php new file mode 100644 index 000000000..15b062faf --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentMethodEnabledByIdentifierCheckerTest.php @@ -0,0 +1,85 @@ +method = $this->createMock(PaymentMethodInterface::class); + + $this->shippingMethodProvider = $this->createMock(PaymentMethodProviderInterface::class); + + $this->shippingMethodEnabledByIdentifierChecker = new PaymentMethodEnabledByIdentifierChecker( + $this->shippingMethodProvider + ); + } + + public function testIsEnabledForEnabledMethod() + { + $identifier = 'shipping_method_1'; + + $this->method + ->expects(static::once()) + ->method('isEnabled') + ->willReturn(true); + + $this->shippingMethodProvider + ->expects(static::any()) + ->method('getPaymentMethod') + ->with($identifier) + ->willReturn($this->method); + + $this->assertTrue($this->shippingMethodEnabledByIdentifierChecker->isEnabled($identifier)); + } + + public function testIsEnabledForDisabledMethod() + { + $identifier = 'shipping_method_1'; + + $this->method + ->expects(static::once()) + ->method('isEnabled') + ->willReturn(false); + + $this->shippingMethodProvider + ->expects(static::any()) + ->method('getPaymentMethod') + ->with($identifier) + ->willReturn($this->method); + + $this->assertFalse($this->shippingMethodEnabledByIdentifierChecker->isEnabled($identifier)); + } + + public function testIsEnabledForNotExistingMethod() + { + $identifier = 'shipping_method_1'; + + $this->shippingMethodProvider + ->expects(static::any()) + ->method('getPaymentMethod') + ->with($identifier) + ->willReturn(null); + + $this->assertFalse($this->shippingMethodEnabledByIdentifierChecker->isEnabled($identifier)); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentRuleEnabledCheckerTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentRuleEnabledCheckerTest.php new file mode 100644 index 000000000..b569312f4 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Checker/PaymentRuleEnabledCheckerTest.php @@ -0,0 +1,63 @@ +methodEnabledChecker = $this->createMock( + PaymentMethodEnabledByIdentifierCheckerInterface::class + ); + + $this->ruleChecker = new PaymentRuleEnabledChecker($this->methodEnabledChecker); + } + + public function testCanBeEnabledForOneEnabledMethod() + { + $this->methodEnabledChecker->expects(static::at(1)) + ->method('isEnabled') + ->willReturn(true); + + $rule = $this->getRuleMock(); + + static::assertTrue($this->ruleChecker->canBeEnabled($rule)); + } + + public function testCanBeEnabledForNoEnabledMethods() + { + $rule = $this->getRuleMock(); + + static::assertFalse($this->ruleChecker->canBeEnabled($rule)); + } + + /** + * @return PaymentMethodsConfigsRule|\PHPUnit\Framework\MockObject\MockObject + */ + private function getRuleMock() + { + $rule = $this->createMock(PaymentMethodsConfigsRule::class); + $rule->expects(static::any()) + ->method('getMethodConfigs') + ->willReturn([ + new PaymentMethodConfig(), new PaymentMethodConfig() + ]); + + return $rule; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/HasApplicablePaymentMethodsTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/HasApplicablePaymentMethodsTest.php new file mode 100644 index 000000000..9baec11c3 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/HasApplicablePaymentMethodsTest.php @@ -0,0 +1,144 @@ +paymentMethodProvider = $this->createMock(PaymentMethodProviderInterface::class); + + $this->paymentMethodsViewsProvider = $this + ->getMockBuilder(PaymentMethodsViewsProviderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->condition = new HasApplicablePaymentMethods( + $this->paymentMethodProvider, + $this->paymentMethodsViewsProvider + ); + } + + protected function tearDown() + { + unset($this->condition, $this->paymentMethodProvider); + } + + public function testGetName() + { + $this->assertEquals(HasApplicablePaymentMethods::NAME, $this->condition->getName()); + } + + /** + * @expectedException \Oro\Component\ConfigExpression\Exception\InvalidArgumentException + * @expectedExceptionMessage Missing "paymentContext" option + */ + public function testInitializeInvalid() + { + $this->assertInstanceOf( + 'Oro\Component\ConfigExpression\Condition\AbstractCondition', + $this->condition->initialize([]) + ); + } + + public function testInitialize() + { + $this->assertInstanceOf( + 'Oro\Component\ConfigExpression\Condition\AbstractCondition', + $this->condition->initialize([self::METHOD, new \stdClass()]) + ); + } + + /** + * @dataProvider evaluateProvider + * @param array $methods + * @param bool $expected + */ + public function testEvaluate($methods, $expected) + { + $method = $this->createMock('Marello\Bundle\PaymentBundle\Method\PaymentMethodInterface'); + $this->paymentMethodProvider->expects($this->any())->method('getPaymentMethod')->willReturn($method); + + $this->paymentMethodsViewsProvider->expects($this->once()) + ->method('getApplicableMethodsViews') + ->willReturn($methods); + + $this->condition->initialize(['paymentContext' => new PaymentContext([])]); + $this->assertEquals($expected, $this->condition->evaluate([])); + } + + /** + * @return array + */ + public function evaluateProvider() + { + return [ + 'no_rules_no_methods' => [ + 'methods' => [], + 'expected' => false, + ], + 'with_rules_no_methods' => [ + 'methods' => [], + 'expected' => false, + ], + 'with_rules_and_methods' => [ + 'methods' => ['flat_rate'], + 'expected' => true, + ], + ]; + } + + public function testToArray() + { + $stdClass = new \stdClass(); + $this->condition->initialize(['paymentContext' => $stdClass]); + $result = $this->condition->toArray(); + + $key = '@' . HasApplicablePaymentMethods::NAME; + + $this->assertInternalType('array', $result); + $this->assertArrayHasKey($key, $result); + + $resultSection = $result[$key]; + $this->assertInternalType('array', $resultSection); + $this->assertArrayHasKey('parameters', $resultSection); + $this->assertContains($stdClass, $resultSection['parameters']); + } + + public function testCompile() + { + $toStringStub = new ToStringStub(); + $options = ['paymentContext' => $toStringStub]; + + $this->condition->initialize($options); + $result = $this->condition->compile('$factory'); + $this->assertEquals( + sprintf( + '$factory->create(\'%s\', [%s])', + HasApplicablePaymentMethods::NAME, + $toStringStub + ), + $result + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/PaymentMethodHasPaymentRulesTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/PaymentMethodHasPaymentRulesTest.php new file mode 100644 index 000000000..6603f4031 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/PaymentMethodHasPaymentRulesTest.php @@ -0,0 +1,148 @@ +repository = $this->getMockBuilder(PaymentMethodsConfigsRuleRepository::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->propertyPath = $this->createMock(PropertyPathInterface::class); + $this->propertyPath->expects($this->any()) + ->method('__toString') + ->will($this->returnValue(self::PROPERTY_PATH_NAME)); + $this->propertyPath->expects($this->any()) + ->method('getElements') + ->will($this->returnValue([self::PROPERTY_PATH_NAME])); + + $this->paymentMethodHasPaymentRulesCondition = new PaymentMethodHasPaymentRules($this->repository); + } + + public function testGetName() + { + $this->assertEquals( + PaymentMethodHasPaymentRules::NAME, + $this->paymentMethodHasPaymentRulesCondition->getName() + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing "method_identifier" option + */ + public function testInitializeInvalid() + { + $this->assertInstanceOf( + PaymentMethodHasPaymentRules::class, + $this->paymentMethodHasPaymentRulesCondition->initialize([]) + ); + } + + public function testInitialize() + { + $this->assertInstanceOf( + PaymentMethodHasPaymentRules::class, + $this->paymentMethodHasPaymentRulesCondition->initialize(['method_identifier']) + ); + } + + /** + * @dataProvider evaluateProvider + * + * @param PaymentMethodsConfigsRule[] $rules + * @param bool $expected + */ + public function testEvaluate($rules, $expected) + { + $this->repository->expects(static::once()) + ->method('getRulesByMethod') + ->willReturn($rules); + + $this->paymentMethodHasPaymentRulesCondition->initialize(['method_identifier']); + $this->assertEquals($expected, $this->paymentMethodHasPaymentRulesCondition->evaluate([])); + } + + /** + * @return array + */ + public function evaluateProvider() + { + return [ + 'no_rules' => [ + 'rules' => [], + 'expected' => false, + ], + 'with_rules' => [ + 'rules' => [ + new PaymentMethodsConfigsRule(), + new PaymentMethodsConfigsRule(), + ], + 'expected' => true, + ], + ]; + } + + public function testToArray() + { + $result = $this->paymentMethodHasPaymentRulesCondition->initialize([$this->propertyPath])->toArray(); + + $this->assertEquals( + sprintf('$%s', self::PROPERTY_PATH_NAME), + $result['@marello_payment_method_has_payment_rules']['parameters'][0] + ); + } + + public function testCompile() + { + $result = $this->paymentMethodHasPaymentRulesCondition->compile('$factoryAccessor'); + + $this->assertContains('$factoryAccessor->create(\'marello_payment_method_has_payment_rules\'', $result); + } + + public function testSetContextAccessor() + { + /** @var ContextAccessorInterface|\PHPUnit\Framework\MockObject\MockObject $contextAccessor * */ + $contextAccessor = $this->getMockBuilder(ContextAccessorInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->paymentMethodHasPaymentRulesCondition->setContextAccessor($contextAccessor); + + $reflection = new \ReflectionProperty( + get_class($this->paymentMethodHasPaymentRulesCondition), + 'contextAccessor' + ); + $reflection->setAccessible(true); + + $this->assertInstanceOf( + get_class($contextAccessor), + $reflection->getValue($this->paymentMethodHasPaymentRulesCondition) + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/ToStringStub.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/ToStringStub.php new file mode 100644 index 000000000..d680a605b --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Condition/ToStringStub.php @@ -0,0 +1,12 @@ +priceMock = $this->createMock(Price::class); + + $this->productHolderMock = $this->createMock(OrderItem::class); + + $this->productHolderMock->method('getId')->willReturn(static::TEST_ENTITY_ID); + + $this->productMock = $this->createMock(Product::class); + + $this->productMock->method('getSku')->willReturn(static::TEST_PRODUCT_SKU); + $this->productMock->method('getId')->willReturn(static::TEST_PRODUCT_ID); + } + + /** + * @return array + */ + protected function getPaymentLineItemParams() + { + return [ + PaymentLineItem::FIELD_PRICE => $this->priceMock, + PaymentLineItem::FIELD_QUANTITY => self::TEST_QUANTITY, + PaymentLineItem::FIELD_PRODUCT_HOLDER => $this->productHolderMock, + PaymentLineItem::FIELD_PRODUCT => $this->productMock, + PaymentLineItem::FIELD_PRODUCT_SKU => self::TEST_PRODUCT_SKU, + PaymentLineItem::FIELD_WEIGHT => self::TEST_WEIGHT, + PaymentLineItem::FIELD_ENTITY_IDENTIFIER => self::TEST_ENTITY_ID, + ]; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/BasicPaymentContextBuilderTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/BasicPaymentContextBuilderTest.php new file mode 100644 index 000000000..dfaed1b70 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/BasicPaymentContextBuilderTest.php @@ -0,0 +1,208 @@ +customerMock = $this->createMock(Customer::class); + $this->lineItemsCollectionMock = $this->createMock(PaymentLineItemCollectionInterface::class); + $this->lineItemsCollectionMock + ->expects(static::any()) + ->method('toArray') + ->willReturn([$this->createMock(PaymentLineItemInterface::class)]); + $this->billingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingOriginMock = $this->createMock(AddressInterface::class); + $this->subtotalMock = $this->createMock(Price::class); + $this->sourceEntityMock = $this->createMock(Order::class); + $this->shippingOriginMock = $this->createMock(AddressInterface::class); + $this->paymentLineItemCollectionFactory = $this->createMock(PaymentLineItemCollectionFactoryInterface::class); + $this->paymentLineItemCollectionFactory + ->expects(static::any()) + ->method('createPaymentLineItemCollection') + ->willReturn($this->lineItemsCollectionMock); + } + + public function testFullContextBuilding() + { + $paymentMethod = 'paymentMethod'; + $currency = 'usd'; + $entityId = '12'; + + $builder = new BasicPaymentContextBuilder( + $this->sourceEntityMock, + $entityId, + $this->paymentLineItemCollectionFactory + ); + + $builder + ->setCurrency($currency) + ->setSubTotal($this->subtotalMock) + ->setLineItems($this->lineItemsCollectionMock) + ->setShippingAddress($this->shippingAddressMock) + ->setBillingAddress($this->billingAddressMock) + ->setCustomer($this->customerMock) + ->setPaymentMethod($paymentMethod) + ->setShippingOrigin($this->shippingOriginMock); + + $expectedContext = $this->getExpectedFullContext( + $paymentMethod, + $currency, + $entityId, + $this->shippingOriginMock + ); + $context = $builder->getResult(); + + $this->assertEquals($expectedContext, $context); + } + + public function testOptionalFields() + { + $entityId = '12'; + + $builder = new BasicPaymentContextBuilder( + $this->sourceEntityMock, + $entityId, + $this->paymentLineItemCollectionFactory + ); + $builder->setShippingOrigin($this->shippingOriginMock); + + $expectedContext = $this->getExpectedContextWithoutOptionalFields( + $entityId, + $this->shippingOriginMock + ); + + $context = $builder->getResult(); + + $this->assertEquals($expectedContext, $context); + } + + public function testWithoutOrigin() + { + $paymentMethod = 'paymentMethod'; + $currency = 'usd'; + $entityId = '12'; + + $builder = new BasicPaymentContextBuilder( + $this->sourceEntityMock, + $entityId, + $this->paymentLineItemCollectionFactory + ); + + $builder + ->setCurrency($currency) + ->setSubTotal($this->subtotalMock) + ->setLineItems($this->lineItemsCollectionMock) + ->setShippingAddress($this->shippingAddressMock) + ->setBillingAddress($this->billingAddressMock) + ->setCustomer($this->customerMock) + ->setPaymentMethod($paymentMethod); + + $expectedContext = $this->getExpectedFullContext( + $paymentMethod, + $currency, + $entityId, + null + ); + $context = $builder->getResult(); + + $this->assertEquals($expectedContext, $context); + } + + /** + * @param string $paymentMethod + * @param string $currency + * @param int $entityId + * @param AddressInterface|null $shippingOrigin + * + * @return PaymentContext + */ + private function getExpectedFullContext($paymentMethod, $currency, $entityId, AddressInterface $shippingOrigin = null) + { + $params = [ + PaymentContext::FIELD_CUSTOMER => $this->customerMock, + PaymentContext::FIELD_LINE_ITEMS => $this->lineItemsCollectionMock, + PaymentContext::FIELD_BILLING_ADDRESS => $this->billingAddressMock, + PaymentContext::FIELD_SHIPPING_ADDRESS => $this->shippingAddressMock, + PaymentContext::FIELD_SHIPPING_ORIGIN => $shippingOrigin, + PaymentContext::FIELD_PAYMENT_METHOD => $paymentMethod, + PaymentContext::FIELD_CURRENCY => $currency, + PaymentContext::FIELD_SUBTOTAL => $this->subtotalMock, + PaymentContext::FIELD_SOURCE_ENTITY => $this->sourceEntityMock, + PaymentContext::FIELD_SOURCE_ENTITY_ID => $entityId, + ]; + $params = array_diff_key($params, array_filter($params, 'is_null')); + + return new PaymentContext($params); + } + + /** + * @param int $entityId + * @param AddressInterface $shippingOrigin + * + * @return PaymentContext + */ + private function getExpectedContextWithoutOptionalFields($entityId, AddressInterface $shippingOrigin) + { + $params = [ + PaymentContext::FIELD_LINE_ITEMS => $this->lineItemsCollectionMock, + PaymentContext::FIELD_SHIPPING_ORIGIN => $shippingOrigin, + PaymentContext::FIELD_SOURCE_ENTITY => $this->sourceEntityMock, + PaymentContext::FIELD_SOURCE_ENTITY_ID => $entityId, + ]; + $params = array_diff_key($params, array_filter($params, 'is_null')); + + return new PaymentContext($params); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactoryTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactoryTest.php new file mode 100644 index 000000000..c5d56eb5f --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicPaymentContextBuilderFactoryTest.php @@ -0,0 +1,46 @@ +lineItemsCollectionFactoryMock = $this->createMock(PaymentLineItemCollectionFactoryInterface::class); + $this->sourceEntityMock = $this->createMock(Order::class); + } + + public function testCreateBuilder() + { + $entityId = '12'; + + $builderFactory = new BasicPaymentContextBuilderFactory($this->lineItemsCollectionFactoryMock); + + $builder = $builderFactory->createPaymentContextBuilder( + $this->sourceEntityMock, + $entityId + ); + + $expectedBuilder = new BasicPaymentContextBuilder( + $this->sourceEntityMock, + $entityId, + $this->lineItemsCollectionFactoryMock + ); + + $this->assertEquals($expectedBuilder, $builder); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicPaymentLineItemBuilderTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicPaymentLineItemBuilderTest.php new file mode 100644 index 000000000..74ba06c27 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicPaymentLineItemBuilderTest.php @@ -0,0 +1,48 @@ +productHolderMock + ); + + $builder + ->setProduct($this->productMock) + ->setPrice($this->priceMock) + ->setProductSku(self::TEST_PRODUCT_SKU) + ->setWeight(self::TEST_WEIGHT); + + $shippingLineItem = $builder->getResult(); + + $expectedPaymentLineItem = new PaymentLineItem($this->getPaymentLineItemParams()); + + $this->assertEquals($expectedPaymentLineItem, $shippingLineItem); + } + + public function testOptionalBuild() + { + $builder = new BasicPaymentLineItemBuilder( + self::TEST_QUANTITY, + $this->productHolderMock + ); + + $shippingLineItem = $builder->getResult(); + + $expectedPaymentLineItem = new PaymentLineItem([ + PaymentLineItem::FIELD_QUANTITY => self::TEST_QUANTITY, + PaymentLineItem::FIELD_PRODUCT_HOLDER => $this->productHolderMock, + PaymentLineItem::FIELD_ENTITY_IDENTIFIER => self::TEST_ENTITY_ID + ]); + + $this->assertEquals($expectedPaymentLineItem, $shippingLineItem); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactoryTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactoryTest.php new file mode 100644 index 000000000..60ab2d9dc --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicPaymentLineItemBuilderFactoryTest.php @@ -0,0 +1,39 @@ +productHolderMock = $this->createMock(OrderItem::class); + } + + public function testCreate() + { + $quantity = 15; + + $builderFactory = new BasicPaymentLineItemBuilderFactory(); + + $builder = $builderFactory->createBuilder( + $quantity, + $this->productHolderMock + ); + + $expectedBuilder = new BasicPaymentLineItemBuilder( + $quantity, + $this->productHolderMock + ); + + $this->assertEquals($expectedBuilder, $builder); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionFactoryTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionFactoryTest.php new file mode 100644 index 000000000..b1e075300 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionFactoryTest.php @@ -0,0 +1,42 @@ +createPaymentLineItemCollection($paymentLineItems); + + $this->assertEquals($paymentLineItems, $collection->toArray()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Expected: Marello\Bundle\PaymentBundle\Context\PaymentLineItemInterface + */ + public function testFactoryWithException() + { + $paymentLineItems = [ + new OrderItem(), + new OrderItem(), + new OrderItem(), + new OrderItem(), + ]; + + $collectionFactory = new DoctrinePaymentLineItemCollectionFactory(); + $collectionFactory->createPaymentLineItemCollection($paymentLineItems); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionTest.php new file mode 100644 index 000000000..9366966fa --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrinePaymentLineItemCollectionTest.php @@ -0,0 +1,23 @@ +assertEquals($paymentLineItems, $collection->toArray()); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextMockTrait.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextMockTrait.php new file mode 100644 index 000000000..0fabb40c3 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextMockTrait.php @@ -0,0 +1,19 @@ +createMock(PaymentContextInterface::class); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextTest.php new file mode 100644 index 000000000..ae09e8f85 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentContextTest.php @@ -0,0 +1,102 @@ +customerMock = $this->getMockBuilder(Customer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->lineItemsCollectionMock = $this->createMock(PaymentLineItemCollectionInterface::class); + $this->billingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingOriginMock = $this->createMock(AddressInterface::class); + $this->subtotalMock = $this->getMockBuilder(Price::class) + ->disableOriginalConstructor() + ->getMock(); + $this->sourceEntityMock = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testConstructionAndGetters() + { + $paymentMethod = 'paymentMethod'; + $currency = 'usd'; + $entityId = '12'; + + $params = [ + PaymentContext::FIELD_CUSTOMER => $this->customerMock, + PaymentContext::FIELD_LINE_ITEMS => $this->lineItemsCollectionMock, + PaymentContext::FIELD_BILLING_ADDRESS => $this->billingAddressMock, + PaymentContext::FIELD_SHIPPING_ADDRESS => $this->shippingAddressMock, + PaymentContext::FIELD_SHIPPING_ORIGIN => $this->shippingOriginMock, + PaymentContext::FIELD_PAYMENT_METHOD => $paymentMethod, + PaymentContext::FIELD_CURRENCY => $currency, + PaymentContext::FIELD_SUBTOTAL => $this->subtotalMock, + PaymentContext::FIELD_SOURCE_ENTITY => $this->sourceEntityMock, + PaymentContext::FIELD_SOURCE_ENTITY_ID => $entityId, + ]; + + $shippingContext = new PaymentContext($params); + + $getterValues = [ + PaymentContext::FIELD_CUSTOMER => $shippingContext->getCustomer(), + PaymentContext::FIELD_LINE_ITEMS => $shippingContext->getLineItems(), + PaymentContext::FIELD_BILLING_ADDRESS => $shippingContext->getBillingAddress(), + PaymentContext::FIELD_SHIPPING_ADDRESS => $shippingContext->getShippingAddress(), + PaymentContext::FIELD_SHIPPING_ORIGIN => $shippingContext->getShippingOrigin(), + PaymentContext::FIELD_PAYMENT_METHOD => $shippingContext->getPaymentMethod(), + PaymentContext::FIELD_CURRENCY => $shippingContext->getCurrency(), + PaymentContext::FIELD_SUBTOTAL => $shippingContext->getSubtotal(), + PaymentContext::FIELD_SOURCE_ENTITY => $shippingContext->getSourceEntity(), + PaymentContext::FIELD_SOURCE_ENTITY_ID => $shippingContext->getSourceEntityIdentifier(), + ]; + + $this->assertEquals($params, $getterValues); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentLineItemTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentLineItemTest.php new file mode 100644 index 000000000..aa841e6f1 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Context/PaymentLineItemTest.php @@ -0,0 +1,35 @@ +getPaymentLineItemParams(); + + $shippingLineItem = new PaymentLineItem($shippingLineItemParams); + + $this->assertEquals($shippingLineItemParams[PaymentLineItem::FIELD_PRICE], $shippingLineItem->getPrice()); + $this->assertEquals( + $shippingLineItemParams[PaymentLineItem::FIELD_QUANTITY], + $shippingLineItem->getQuantity() + ); + $this->assertEquals( + $shippingLineItemParams[PaymentLineItem::FIELD_PRODUCT_HOLDER], + $shippingLineItem->getProductHolder() + ); + $this->assertEquals($shippingLineItemParams[PaymentLineItem::FIELD_PRODUCT], $shippingLineItem->getProduct()); + $this->assertEquals( + $shippingLineItemParams[PaymentLineItem::FIELD_PRODUCT_SKU], + $shippingLineItem->getProductSku() + ); + $this->assertEquals($shippingLineItemParams[PaymentLineItem::FIELD_WEIGHT], $shippingLineItem->getWeight()); + $this->assertEquals( + $shippingLineItemParams[PaymentLineItem::FIELD_ENTITY_IDENTIFIER], + $shippingLineItem->getEntityIdentifier() + ); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Converter/Basic/PaymentContextToRuleValuesConverterTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Converter/Basic/PaymentContextToRuleValuesConverterTest.php new file mode 100644 index 000000000..e6b7b3ebf --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Converter/Basic/PaymentContextToRuleValuesConverterTest.php @@ -0,0 +1,107 @@ +factory = new DecoratedProductLineItemFactory( + $this->createMock(VirtualFieldsProductDecoratorFactory::class) + ); + + $this->shippingContextToRuleValuesConverter = new BasicPaymentContextToRulesValueConverter( + $this->factory + ); + } + + /** + * @dataProvider convertDataProvider + * @param PaymentContext $context + */ + public function testConvert(PaymentContext $context) + { + $expectedValues = [ + 'lineItems' => array_map(function (PaymentLineItem $lineItem) use ($context) { + return $this->factory + ->createLineItemWithDecoratedProductByLineItem($context->getLineItems()->toArray(), $lineItem); + }, $context->getLineItems()->toArray()), + 'shippingOrigin' => $context->getShippingOrigin(), + 'billingAddress' => $context->getBillingAddress(), + 'shippingAddress' => $context->getShippingAddress(), + 'paymentMethod' => $context->getPaymentMethod(), + 'currency' => $context->getCurrency(), + 'subtotal' => $context->getSubtotal(), + 'customer' => $context->getCustomer(), + 'company' => $context->getCompany(), + 'total' => $context->getTotal() + ]; + $this->assertEquals($expectedValues, $this->shippingContextToRuleValuesConverter->convert($context)); + } + + /** + * @return array + */ + public function convertDataProvider() + { + return [ + [ + 'context' => new PaymentContext([ + PaymentContext::FIELD_LINE_ITEMS => new DoctrinePaymentLineItemCollection([ + new PaymentLineItem([ + PaymentLineItem::FIELD_PRODUCT => $this->getEntity(Product::class, ['id' => 1]), + ]), + ]), + PaymentContext::FIELD_SHIPPING_ORIGIN => $this->getEntity(ShippingAddressStub::class, [ + 'region' => $this->getEntity(Region::class, [ + 'code' => 'CA', + ], ['US-CA']), + ]), + PaymentContext::FIELD_BILLING_ADDRESS => $this->getEntity(ShippingAddressStub::class, [ + 'country' => new Country('US'), + ]), + PaymentContext::FIELD_SHIPPING_ADDRESS => $this->getEntity(ShippingAddressStub::class, [ + 'country' => new Country('US'), + 'region' => $this->getEntity(Region::class, [ + 'code' => 'CA', + ], ['US-CA']), + 'postalCode' => '90401', + ]), + PaymentContext::FIELD_PAYMENT_METHOD => 'integration_payment_method', + PaymentContext::FIELD_CURRENCY => 'USD', + PaymentContext::FIELD_SUBTOTAL => Price::create(10.0, 'USD'), + PaymentContext::FIELD_CUSTOMER => (new Customer())->setFirstName('Customer Name'), + ]), + ], + ]; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodConfigTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodConfigTest.php new file mode 100644 index 000000000..0b92579ea --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodConfigTest.php @@ -0,0 +1,26 @@ + 'test']], + ['methodsConfigsRule', new PaymentMethodsConfigsRule()], + ]; + + $entity = new PaymentMethodConfig(); + + $this->assertPropertyAccessors($entity, $properties); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleDestinationPostalCodeTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleDestinationPostalCodeTest.php new file mode 100644 index 000000000..49771e1ce --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleDestinationPostalCodeTest.php @@ -0,0 +1,26 @@ +country = $this->createMockCountry(); + $this->region = $this->createMockRegion(); + + $this->shippingRuleDestination = $this->getEntity( + 'Marello\Bundle\PaymentBundle\Entity\PaymentMethodsConfigsRuleDestination', + [ + 'region' => $this->region, + 'country' => $this->country, + 'postalCodes' => new ArrayCollection([$this->createPostalCode('123')]), + ] + ); + } + + public function testProperties() + { + $properties = [ + ['id', 1], + ['region', new Region('code')], + ['regionText', 'text'], + ['country', new Country('UA')], + ['methodsConfigsRule', new PaymentMethodsConfigsRule()], + ]; + + $destination = new PaymentMethodsConfigsRuleDestination(); + static::assertPropertyAccessors($destination, $properties); + static::assertPropertyCollection( + $destination, + 'postalCodes', + $this->createPostalCode('123') + ); + } + + public function testGetRegionName() + { + $this->assertEquals('RegionName', $this->shippingRuleDestination->getRegionName()); + $this->shippingRuleDestination->setRegion(null); + $this->assertEquals('', $this->shippingRuleDestination->getRegionName()); + } + + public function testGetRegionCode() + { + $this->assertEquals('RegionCode', $this->shippingRuleDestination->getRegionCode()); + $this->shippingRuleDestination->setRegion(null); + $this->assertEquals('', $this->shippingRuleDestination->getRegionCode()); + } + + public function testGetCountryName() + { + $this->assertEquals('CountryName', $this->shippingRuleDestination->getCountryName()); + $this->shippingRuleDestination->setCountry(null); + $this->assertEquals('', $this->shippingRuleDestination->getCountryName()); + } + + public function testGetCountryIso2() + { + $this->assertEquals('CountryIso2', $this->shippingRuleDestination->getCountryIso2()); + $this->shippingRuleDestination->setCountry(null); + $this->assertEquals('', $this->shippingRuleDestination->getCountryIso2()); + } + + public function testGetCountryIso3() + { + $this->assertEquals('CountryIso3', $this->shippingRuleDestination->getCountryIso3()); + $this->shippingRuleDestination->setCountry(null); + $this->assertEquals('', $this->shippingRuleDestination->getCountryIso3()); + } + + /** + * @dataProvider toStringDataProvider + * + * @param array $data + * @param string $expectedString + */ + public function testToString(array $data, $expectedString) + { + $entity = (string) $this->getEntity( + 'Marello\Bundle\PaymentBundle\Entity\PaymentMethodsConfigsRuleDestination', + $data + ); + $this->assertEquals($expectedString, $entity); + } + + /** + * @return array + */ + public function toStringDataProvider() + { + return [ + 'all' => [ + 'data' => [ + 'country' => $this->createMockCountry(), + 'region' => $this->createMockRegion(), + 'postalCodes' => new ArrayCollection([$this->createPostalCode('12345')]), + ], + 'expectedString' => 'RegionName, CountryName 12345' + ], + 'country and postal code' => [ + 'data' => [ + 'country' => $this->createMockCountry(), + 'region' => null, + 'postalCodes' => new ArrayCollection([ + $this->createPostalCode('12345'), + $this->createPostalCode('54321'), + ]), + ], + 'expectedString' => 'CountryName 12345, 54321' + ], + 'country and region' => [ + 'data' => [ + 'country' => $this->createMockCountry('SecondCountryName'), + 'region' => $this->createMockRegion('SecondRegionName'), + 'postalCodes' => new ArrayCollection(), + ], + 'expectedString' => 'SecondRegionName, SecondCountryName' + ], + 'only country' => [ + 'data' => [ + 'country' => $this->createMockCountry(), + 'region' => null, + 'postalCodes' => new ArrayCollection(), + ], + 'expectedString' => 'CountryName' + ] + ]; + } + + /** + * @param string $name + * @param string $iso2 + * @param string $iso3 + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function createMockCountry($name = 'CountryName', $iso2 = 'CountryIso2', $iso3 = 'CountryIso3') + { + $result = $this->getMockBuilder('Oro\Bundle\AddressBundle\Entity\Country') + ->disableOriginalConstructor() + ->getMock(); + $result->expects($this->any()) + ->method('__toString') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getIso2Code') + ->will($this->returnValue($iso2)); + $result->expects($this->any()) + ->method('getIso3Code') + ->will($this->returnValue($iso3)); + + return $result; + } + + /** + * @param string $name + * @param string $code + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function createMockRegion($name = 'RegionName', $code = 'RegionCode') + { + $result = $this->createMock('Oro\Bundle\AddressBundle\Entity\Region', [], ['combinedCode']); + $result->expects($this->any()) + ->method('__toString') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getCode') + ->will($this->returnValue($code)); + return $result; + } + + /** + * @param string $name + * @return PaymentMethodsConfigsRuleDestinationPostalCode + */ + protected function createPostalCode($name) + { + return (new PaymentMethodsConfigsRuleDestinationPostalCode())->setName($name); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleMockTrait.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleMockTrait.php new file mode 100644 index 000000000..0f4d3552a --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleMockTrait.php @@ -0,0 +1,19 @@ +createMock(PaymentMethodsConfigsRule::class); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleTest.php new file mode 100644 index 000000000..01aaa8dc1 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Entity/PaymentMethodsConfigsRuleTest.php @@ -0,0 +1,32 @@ +paymentMethodProvider = new CompositePaymentMethodProvider(); + + $this->provider = $this->getMockBuilder(PaymentMethodProviderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testGetMethods() + { + $paymentMethods = $this->paymentMethodProvider->getPaymentMethods(); + $this->assertInternalType('array', $paymentMethods); + $this->assertEmpty($paymentMethods); + } + + public function testRegistry() + { + $method = $this->createMock(PaymentMethodInterface::class); + + $this->provider->expects($this->once()) + ->method('getPaymentMethods') + ->willReturn(['test_name' => $method]); + + $this->provider->expects($this->once()) + ->method('getPaymentMethod') + ->with('test_name') + ->willReturn($method); + + $this->provider->expects($this->once()) + ->method('hasPaymentMethod') + ->with('test_name') + ->willReturn(true); + + $this->paymentMethodProvider->addProvider($this->provider); + $this->assertEquals($method, $this->paymentMethodProvider->getPaymentMethod('test_name')); + $this->assertEquals(['test_name' => $method], $this->paymentMethodProvider->getPaymentMethods()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage There is no payment method for "wrong_name" identifier + */ + public function testRegistryWrongMethod() + { + $this->paymentMethodProvider->getPaymentMethod('wrong_name'); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php new file mode 100644 index 000000000..e98c800e8 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php @@ -0,0 +1,37 @@ +eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $this->dispatcher = new BasicMethodRemovalEventDispatcher($this->eventDispatcher); + } + + public function testDispatch() + { + $methodId = 'method'; + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(MethodRemovalEvent::NAME, new MethodRemovalEvent($methodId)); + + $this->dispatcher->dispatch($methodId); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php new file mode 100644 index 000000000..d6b926e61 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php @@ -0,0 +1,38 @@ +eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $this->dispatcher = new BasicMethodRenamingEventDispatcher($this->eventDispatcher); + } + + public function testDispatch() + { + $oldId = 'old_id'; + $newId = 'new_id'; + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(MethodRenamingEvent::NAME, new MethodRenamingEvent($oldId, $newId)); + + $this->dispatcher->dispatch($oldId, $newId); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php new file mode 100644 index 000000000..135a5c21c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php @@ -0,0 +1,17 @@ +assertSame($methodId, $event->getMethodIdentifier()); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php new file mode 100644 index 000000000..ec365e1eb --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php @@ -0,0 +1,19 @@ +assertSame($oldId, $event->getOldMethodIdentifier()); + $this->assertSame($newId, $event->getNewMethodIdentifier()); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php new file mode 100644 index 000000000..f03ff07da --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php @@ -0,0 +1,96 @@ +channelType = 'payment_method'; + $this->identifierGenerator = $this->createMock(IntegrationIdentifierGeneratorInterface::class); + $this->dispatcher = $this->createMock(MethodRemovalEventDispatcherInterface::class); + + $this->listener = new IntegrationRemovalListener( + $this->channelType, + $this->identifierGenerator, + $this->dispatcher + ); + } + + public function testPreRemove() + { + /** @var Channel|\PHPUnit\Framework\MockObject\MockObject $channel */ + $channel = $this->createMock(Channel::class); + $channel->expects(static::once()) + ->method('getType') + ->willReturn($this->channelType); + + /** @var ChannelDeleteEvent|\PHPUnit\Framework\MockObject\MockObject $event */ + $event = $this->createMock(ChannelDeleteEvent::class); + $event->expects(static::any()) + ->method('getChannel') + ->willReturn($channel); + + $identifier = 'method'; + + $this->identifierGenerator->expects(static::once()) + ->method('generateIdentifier') + ->with($channel) + ->willReturn($identifier); + + $this->dispatcher->expects(static::once()) + ->method('dispatch') + ->with($identifier); + + $this->listener->onRemove($event); + } + + public function testPreRemoveOtherType() + { + /** @var Channel|\PHPUnit\Framework\MockObject\MockObject $channel */ + $channel = $this->createMock(Channel::class); + $channel->expects(static::once()) + ->method('getType') + ->willReturn('other_type'); + + /** @var ChannelDeleteEvent|\PHPUnit\Framework\MockObject\MockObject $event */ + $event = $this->createMock(ChannelDeleteEvent::class); + $event->expects(static::any()) + ->method('getChannel') + ->willReturn($channel); + + $this->identifierGenerator->expects(static::never()) + ->method('generateIdentifier'); + + $this->dispatcher->expects(static::never()) + ->method('dispatch'); + + $this->listener->onRemove($event); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php new file mode 100644 index 000000000..2deeb0088 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php @@ -0,0 +1,59 @@ +paymentMethodConfigRepository = $this->createMock(PaymentMethodConfigRepository::class); + $this->listener = new MethodRenamingListener($this->paymentMethodConfigRepository); + } + + public function testOnMethodRename() + { + $oldId = 'old_name'; + $newId = 'new_name'; + + /** @var MethodRenamingEvent|\PHPUnit\Framework\MockObject\MockObject $event */ + $event = $this->createMock(MethodRenamingEvent::class); + $event->expects(static::any()) + ->method('getOldMethodIdentifier') + ->willReturn($oldId); + + $event->expects(static::any()) + ->method('getNewMethodIdentifier') + ->willReturn($newId); + + $config1 = $this->createMock(PaymentMethodConfig::class); + $config1->expects(static::once()) + ->method('setMethod') + ->with($newId); + $config2 = $this->createMock(PaymentMethodConfig::class); + $config2->expects(static::once()) + ->method('setMethod') + ->with($newId); + + $this->paymentMethodConfigRepository->expects(static::once()) + ->method('findByMethod') + ->with($oldId) + ->willReturn([$config1, $config2]); + + $this->listener->onMethodRename($event); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/PaymentMethodDisableIntegrationListenerTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/PaymentMethodDisableIntegrationListenerTest.php new file mode 100644 index 000000000..81f42964c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/EventListener/PaymentMethodDisableIntegrationListenerTest.php @@ -0,0 +1,111 @@ +channelType = 'integration_payment_method'; + + $this->methodIdentifierGenerator = $this->createMock( + IntegrationIdentifierGeneratorInterface::class + ); + $this->handler = $this->createMock( + PaymentMethodDisableHandlerInterface::class + ); + $this->event = $this->createMock( + ChannelDisableEvent::class + ); + $this->listener = new PaymentMethodDisableIntegrationListener( + $this->channelType, + $this->methodIdentifierGenerator, + $this->handler + ); + } + + public function testOnIntegrationDisable() + { + $methodIdentifier = 'method_1'; + $channel = $this->createMock(Channel::class); + + $this->event + ->expects(static::once()) + ->method('getChannel') + ->willReturn($channel); + + $channel + ->expects(static::once()) + ->method('getType') + ->willReturn($this->channelType); + + $this->methodIdentifierGenerator + ->expects(static::once()) + ->method('generateIdentifier') + ->with($channel) + ->willReturn($methodIdentifier); + + $this->handler + ->expects(static::once()) + ->method('handleMethodDisable') + ->with($methodIdentifier); + + $this->listener->onIntegrationDisable($this->event); + } + + public function testOnIntegrationDisableWithAnotherType() + { + $channel = $this->createMock(Channel::class); + + $this->event + ->expects(static::once()) + ->method('getChannel') + ->willReturn($channel); + + $channel + ->expects(static::once()) + ->method('getType') + ->willReturn('another_type'); + + $this->methodIdentifierGenerator + ->expects(static::never()) + ->method('generateIdentifier'); + + $this->handler + ->expects(static::never()) + ->method('handleMethodDisable'); + + $this->listener->onIntegrationDisable($this->event); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Handler/RulesPaymentMethodDisableHandlerDecoratorTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Handler/RulesPaymentMethodDisableHandlerDecoratorTest.php new file mode 100644 index 000000000..1f790f4a1 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Handler/RulesPaymentMethodDisableHandlerDecoratorTest.php @@ -0,0 +1,151 @@ +handler = $this->createMock(PaymentMethodDisableHandlerInterface::class); + $this->repository = $this->createMock(PaymentMethodsConfigsRuleRepository::class); + $this->paymentMethodProvider = $this->createMock(PaymentMethodProviderInterface::class); + + $this->decorator = new RulesPaymentMethodDisableHandlerDecorator( + $this->handler, + $this->repository, + $this->paymentMethodProvider + ); + } + + /** + * @param string $disabledMethodId + * @param array $configs + * @param array $registryMap + * + * @dataProvider testHandleMethodDisableProvider + */ + public function testHandleMethodDisable($disabledMethodId, $configs, $registryMap) + { + $this->handler->expects(self::once())->method('handleMethodDisable')->with($disabledMethodId); + + $configMocks = []; + $registryMapValues = []; + $methods = []; + foreach ($registryMap as $methodId => $enabled) { + $methods[$methodId] = $this->createMock(PaymentMethodInterface::class); + $methods[$methodId]->expects(self::any())->method('isEnabled')->willReturn($enabled); + $registryMapValues[] = [$methodId, $methods[$methodId]]; + } + + $rules = []; + foreach ($configs as $configName => $config) { + $methodConfigs = []; + foreach ($config['methods'] as $methodId) { + $methodConfig = $this->createMock(PaymentMethodConfig::class); + $methodConfig->expects(self::once())->method('getMethod')->willReturn($methodId); + $methodConfigs[] = $methodConfig; + } + $rules[$configName] = $this->createMock(Rule::class); + $rules[$configName]->expects(self::exactly($config['rule_disabled']))->method('setEnabled')->with(false); + + $configMock = $this->createMock(PaymentMethodsConfigsRule::class); + $configMock->expects(self::once()) + ->method('getMethodConfigs') + ->willReturn($methodConfigs); + $configMock->expects(self::any()) + ->method('getRule') + ->willReturn($rules[$configName]); + $configMocks[] = $configMock; + } + + $this->paymentMethodProvider + ->method('getPaymentMethod') + ->will($this->returnValueMap($registryMapValues)); + + $this->repository->expects(self::once()) + ->method('getEnabledRulesByMethod') + ->willReturn($configMocks); + + $this->decorator->handleMethodDisable($disabledMethodId); + } + + /** + * @return array + */ + public function testHandleMethodDisableProvider() + { + return [ + 'a_few_methods' => + [ + 'methodId' => 'method1', + 'configs' => + [ + 'config1' => + [ + 'methods' => ['method1', 'method2'], + 'rule_disabled' => 1, + ], + 'config2' => + [ + 'methods' => ['method1', 'method3'], + 'rule_disabled' => 0, + ] + ], + 'registry_map' => + [ + 'method1' => true, + 'method2' => false, + 'method3' => true, + ], + ], + 'only_method' => + [ + 'methodId' => 'method1', + 'configs' => + [ + 'config1' => + [ + 'methods' => ['method1'], + 'rule_disabled' => 1, + ], + ], + 'registry_map' => + [ + 'method1' => true, + ], + ], + ]; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewCollectionTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewCollectionTest.php new file mode 100644 index 000000000..e8150352d --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewCollectionTest.php @@ -0,0 +1,212 @@ +createCollection(); + + $methodId = 'someMethodId'; + + $view = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $addResult = $collection->addMethodView($methodId, $view); + + $actualView = $collection->getMethodView($methodId); + + $this->assertEquals($collection, $addResult); + $this->assertNotNull($actualView); + $this->assertEquals($view, $actualView); + } + + public function testGetMethodViewWhenNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + + $actualView = $collection->getMethodView($methodId); + + $this->assertNull($actualView); + } + + public function testAddMethodViewWhenAlreadyExists() + { + $methodId = 'someMethodId'; + + $collection = $this->createCollection(); + + $view = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $view); + + $view2 = [ + 'someField3' => 'someValue4', + 'someField4' => 'someValue4', + 'sortOrder' => 1 + ]; + + $addMethodViewResult = $collection->addMethodView($methodId, $view2); + + $actualView = $collection->getMethodView($methodId); + + $this->assertNotNull($actualView); + $this->assertEquals($view, $actualView); + $this->assertEquals($collection, $addMethodViewResult); + } + + public function testHasMethodView() + { + $methodId = 'someMethodId'; + $collection = $this->createCollection(); + + $collection->addMethodView($methodId, []); + + $this->assertTrue($collection->hasMethodView($methodId)); + } + + public function testHasMethodViewNotExists() + { + $collection = $this->createCollection(); + + $this->assertFalse($collection->hasMethodView('someMethodId')); + } + + public function testRemoveMethodView() + { + $methodId = 'someMethodId'; + $collection = $this->createCollection(); + + $collection->addMethodView($methodId, []); + + $this->assertTrue($collection->hasMethodView($methodId)); + + $removeResult = $collection->removeMethodView($methodId); + + $this->assertEquals($collection, $removeResult); + $this->assertFalse($collection->hasMethodView($methodId)); + } + + public function testRemoveMethodViewWhenNotExists() + { + $methodId = 'someMethodId'; + $collection = $this->createCollection(); + + $removeResult = $collection->removeMethodView($methodId); + + $this->assertEquals($collection, $removeResult); + $this->assertFalse($collection->hasMethodView($methodId)); + } + + public function testGetAllMethodsViews() + { + $collection = $this->createCollection(); + + $this->assertEquals([], $collection->getAllMethodsViews()); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $this->assertEquals([$methodId => $methodView], $collection->getAllMethodsViews()); + + $methodId2 = 'someOtherMethodId'; + + $methodView2 = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId2, $methodView2); + + $this->assertEquals([$methodId => $methodView, $methodId2 => $methodView2], $collection->getAllMethodsViews()); + } + + public function testToArray() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $methodId2 = 'someOtherMethodId'; + + $methodView2 = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId2, $methodView2); + + $this->assertEquals( + [ + $methodId => $methodView, + $methodId2 => $methodView2 + ], + $collection->toArray() + ); + } + + public function testIsEmpty() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $this->assertTrue($collection->isEmpty()); + + $collection->addMethodView($methodId, $methodView); + + $this->assertFalse($collection->isEmpty()); + + $collection->clear(); + + $this->assertTrue($collection->isEmpty()); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewFactoryTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewFactoryTest.php new file mode 100644 index 000000000..20f85b362 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/PaymentMethodViewFactoryTest.php @@ -0,0 +1,53 @@ +paymentMethodProviderMock = $this + ->getMockBuilder(PaymentMethodProviderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->paymentMethodViewFactory = new PaymentMethodViewFactory($this->paymentMethodProviderMock); + } + + public function testCreateMethodView() + { + $methodId = 'someId'; + $label = 'someLabel'; + $sortOrder = 5; + $options = [ + 'option1', + 'option2' + ]; + + $expected = [ + 'identifier' => $methodId, + 'label' => $label, + 'sortOrder' => $sortOrder, + 'options' => $options + ]; + + $actual = $this->paymentMethodViewFactory->createMethodView($methodId, $label, $sortOrder, $options); + + $this->assertEquals($expected, $actual); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Provider/ChannelPaymentMethodProviderTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Provider/ChannelPaymentMethodProviderTest.php new file mode 100644 index 000000000..81c3f9c5f --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Method/Provider/ChannelPaymentMethodProviderTest.php @@ -0,0 +1,136 @@ +doctrineHelper = $this->createMock(DoctrineHelper::class); + $repository = $this->createMock(ChannelRepository::class); + + $this->doctrineHelper + ->method('getEntityRepository') + ->with('OroIntegrationBundle:Channel') + ->willReturn($repository); + + $loadedChannel = $this->createChannel('ch_enabled'); + $fetchedChannel = $this->createChannel('ch_disabled'); + + $this->enabledMethod = $this->createMock(PaymentMethodInterface::class); + $this->enabledMethod + ->method('getIdentifier') + ->willReturn('ups_10'); + + $this->disabledMethod = $this->createMock(PaymentMethodInterface::class); + $this->disabledMethod + ->method('getIdentifier') + ->willReturn('ups_20'); + + $this->methodFactory = $this->createMock(IntegrationPaymentMethodFactoryInterface::class); + $this->methodFactory + ->method('create') + ->will($this->returnValueMap([ + [$loadedChannel, $this->enabledMethod], + [$fetchedChannel, $this->disabledMethod], + ])); + + $this->provider = new ChannelPaymentMethodProvider(static::TYPE, $this->doctrineHelper, $this->methodFactory); + + $doctrineEvent = $this->createLifecycleEventArgsMock(); + $this->provider->postLoad($loadedChannel, $doctrineEvent); + + $repository + ->method('findByTypeAndExclude') + ->will(static::returnCallback(function () use ($fetchedChannel, $doctrineEvent) { + $this->provider->postLoad($fetchedChannel, $doctrineEvent); + return [$fetchedChannel]; + })); + } + + public function testGetPaymentMethods() + { + $methods = $this->provider->getPaymentMethods(); + static::assertCount(2, $methods); + $actualMethod = reset($methods); + static::assertSame($this->enabledMethod, $actualMethod); + } + + public function testGetPaymentMethod() + { + $method = $this->provider->getPaymentMethod($this->enabledMethod->getIdentifier()); + static::assertInstanceOf(PaymentMethodInterface::class, $method); + } + + public function testHasPaymentMethod() + { + static::assertTrue($this->provider->hasPaymentMethod($this->enabledMethod->getIdentifier())); + } + + public function testHasPaymentMethodFalse() + { + static::assertFalse($this->provider->hasPaymentMethod('wrong')); + } + + /** + * @param string $name + * + * @return Channel + */ + private function createChannel($name) + { + return $this->getEntity( + Channel::class, + ['id' => 20, 'name' => $name, 'type' => static::TYPE] + ); + } + + /** + * @return LifecycleEventArgs|\PHPUnit\Framework\MockObject\MockObject + */ + private function createLifecycleEventArgsMock() + { + return $this->createMock(LifecycleEventArgs::class); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/BasicPaymentMethodChoicesProviderTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/BasicPaymentMethodChoicesProviderTest.php new file mode 100644 index 000000000..8e4979a52 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/BasicPaymentMethodChoicesProviderTest.php @@ -0,0 +1,135 @@ +paymentMethodProvider = $this->createMock(PaymentMethodProviderInterface::class); + $this->translator = $this->createMock(TranslatorInterface::class); + $this->choicesProvider = new BasicPaymentMethodChoicesProvider( + $this->paymentMethodProvider, + $this->translator + ); + } + + /** + * @param array $methods + * @param array $result + * @param bool $translate + * + * @dataProvider methodsProvider + */ + public function testGetMethods($methods, $result, $translate = false) + { + $translation = [ + ['bank transfer', [], null, null, 'bank transfer translated'], + ['payment term', [], null, null, 'payment term translated'], + ]; + + $this->paymentMethodProvider->expects($this->once()) + ->method('getPaymentMethods') + ->willReturn($methods); + + $this->translator->expects($this->any()) + ->method('trans') + ->will($this->returnValueMap($translation)); + + $this->assertEquals($result, $this->choicesProvider->getMethods($translate)); + } + + /** + * @return array + */ + public function methodsProvider() + { + return + [ + 'some_methods' => + [ + 'methods' => + [ + 'payment_term' => $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'payment_term', + 'sortOrder' => 1, + 'label' => 'payment term', + 'isEnabled' => false, + 'options' => [], + ] + ), + 'bank_transfer' => $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'bank_transfer', + 'sortOrder' => 1, + 'label' => 'bank transfer', + 'isEnabled' => true, + 'options' => [], + ] + ), + ], + 'result' => ['payment_term' => 'payment term', 'bank_transfer' => 'bank transfer'], + 'translate' => false, + ], + 'some_methods_with_translation' => + [ + 'methods' => + [ + 'bank_transfer' => $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'bank_transfer', + 'sortOrder' => 1, + 'label' => 'bank transfer', + 'isEnabled' => true, + 'options' => [], + ] + ), + 'payment_term' => $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'payment_term', + 'sortOrder' => 1, + 'label' => 'payment term', + 'isEnabled' => false, + 'options' => [], + ] + ), + ], + 'result' => ['bank_transfer' => 'bank transfer translated', 'payment_term' => 'payment term translated'], + 'translate' => true, + ], + 'no_methods' => + [ + 'methods' => [], + 'result' => [], + ], + ]; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/EnabledPaymentMethodChoicesProviderDecoratorTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/EnabledPaymentMethodChoicesProviderDecoratorTest.php new file mode 100644 index 000000000..6e995f9b3 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/EnabledPaymentMethodChoicesProviderDecoratorTest.php @@ -0,0 +1,178 @@ +paymentMethodProvider = $this->createMock(PaymentMethodProviderInterface::class); + $this->choicesProvider = $this->createMock(PaymentMethodChoicesProviderInterface::class); + $this->enabledChoicesProvider = new EnabledPaymentMethodChoicesProviderDecorator( + $this->paymentMethodProvider, + $this->choicesProvider + ); + } + + /** + * @param array $registryMap + * @param array $choices + * @param array $result + * + * @dataProvider methodsProvider + */ + public function testGetMethods($registryMap, $choices, $result) + { + $this->paymentMethodProvider->expects($this->any()) + ->method('getPaymentMethod') + ->will($this->returnValueMap($registryMap)); + + $this->choicesProvider->expects($this->once()) + ->method('getMethods') + ->willReturn($choices); + + $this->assertEquals($result, $this->enabledChoicesProvider->getMethods()); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function methodsProvider() + { + return + [ + 'all_methods_enabled' => + [ + 'methods_map' => + [ + [ + 'bank_transfer', + $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'bank_transfer', + 'sortOrder' => 1, + 'label' => 'bank transfer', + 'isEnabled' => true, + 'options' => ['instructions'], + ] + ), + ], + [ + 'payment_term', + $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'payment_term', + 'sortOrder' => 1, + 'label' => 'payment term', + 'isEnabled' => true, + 'options' => [], + ] + ), + ], + ], + 'choices' => ['payment_term' => 'payment term', 'bank_transfer' => 'bank transfer'], + 'result' => ['payment_term' => 'payment term', 'bank_transfer' => 'bank transfer'], + ], + 'some_methods_disabled' => + [ + 'methods_map' => + [ + [ + 'bank_transfer', + $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'bank_transfer', + 'sortOrder' => 1, + 'label' => 'bank transfer', + 'isEnabled' => true, + 'options' => ['instructions'], + ] + ), + ], + [ + 'payment_term', + $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'payment_term', + 'sortOrder' => 1, + 'label' => 'payment term', + 'isEnabled' => false, + 'options' => [], + ] + ), + ], + ], + 'choices' => ['bank_transfer' => 'bank transfer', 'payment_term' => 'payment term'], + 'result' => ['bank_transfer' => 'bank transfer'], + ], + 'all_disabled_methods' => + [ + 'methods_map' => + [ + [ + 'bank_transfer', + $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'bank_transfer', + 'sortOrder' => 1, + 'label' => 'bank transfer', + 'isEnabled' => false, + 'options' => [], + ] + ), + ], + [ + 'payment_term', + $this->getEntity( + PaymentMethodStub::class, + [ + 'identifier' => 'payment_term', + 'sortOrder' => 1, + 'label' => 'payment term', + 'isEnabled' => false, + 'options' => [], + ] + ), + ], + ], + 'choices' => ['flat rate' => 'flat_rate', 'ups' => 'ups'], + 'result' => [], + ], + 'no_methods' => + [ + 'methods' => [], + 'choices' => [], + 'result' => [], + ], + ]; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php new file mode 100644 index 000000000..fb37624f5 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php @@ -0,0 +1,114 @@ +repository = $this->createMock(PaymentMethodsConfigsRuleRepository::class); + + $this->filtrationService = $this->createMock(MethodsConfigsRulesFiltrationServiceInterface::class); + + $this->provider = new BasicMethodsConfigsRulesByContextProvider( + $this->filtrationService, + $this->repository + ); + } + + public function testGetAllFilteredPaymentMethodsConfigsWithBillingAddress() + { + $currency = 'USD'; + $address = $this->createAddressMock(); + $rulesFromDb = [ + $this->createPaymentMethodsConfigsRuleMock(), + $this->createPaymentMethodsConfigsRuleMock(), + ]; + + $this->repository->expects(static::once()) + ->method('getByDestinationAndCurrency') + ->with($address, $currency) + ->willReturn($rulesFromDb); + + $this->repository->expects(static::never()) + ->method('getByCurrencyWithoutDestination'); + + $context = $this->createPaymentContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + $context->method('getBillingAddress') + ->willReturn($address); + + $expectedRules = [$this->createPaymentMethodsConfigsRuleMock()]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredPaymentMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getPaymentMethodsConfigsRules($context) + ); + } + + public function testGetAllFilteredPaymentMethodsConfigsWithoutShippingAddress() + { + $currency = 'USD'; + $rulesFromDb = [$this->createPaymentMethodsConfigsRuleMock()]; + + $this->repository->expects(static::once()) + ->method('getByCurrencyWithoutDestination') + ->with($currency) + ->willReturn($rulesFromDb); + + $context = $this->createPaymentContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + + $expectedRules = [$this->createPaymentMethodsConfigsRuleMock()]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredPaymentMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getPaymentMethodsConfigsRules($context) + ); + } + + /** + * @return AddressInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createAddressMock() + { + return $this->createMock(AddressInterface::class); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php new file mode 100644 index 000000000..16b011b02 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php @@ -0,0 +1,114 @@ +repository = $this->createMock(PaymentMethodsConfigsRuleRepository::class); + + $this->filtrationService = $this->createMock(MethodsConfigsRulesFiltrationServiceInterface::class); + + $this->provider = new RegardlessDestination\RegardlessDestinationMethodsConfigsRulesByContextProvider( + $this->filtrationService, + $this->repository + ); + } + + public function testGetAllFilteredPaymentMethodsConfigsWithBillingAddress() + { + $currency = 'USD'; + $address = $this->createAddressMock(); + $rulesFromDb = [$this->createPaymentMethodsConfigsRuleMock()]; + + $this->repository->expects(static::once()) + ->method('getByDestinationAndCurrency') + ->with($address, $currency) + ->willReturn($rulesFromDb); + + $this->repository->expects(static::never()) + ->method('getByCurrency'); + + $context = $this->createPaymentContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + $context->method('getBillingAddress') + ->willReturn($address); + + $expectedRules = [ + $this->createPaymentMethodsConfigsRuleMock(), + $this->createPaymentMethodsConfigsRuleMock(), + ]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredPaymentMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getPaymentMethodsConfigsRules($context) + ); + } + + public function testGetAllFilteredPaymentMethodsConfigsWithoutBillingAddress() + { + $currency = 'USD'; + $rulesFromDb = [$this->createPaymentMethodsConfigsRuleMock()]; + + $this->repository->expects(static::once()) + ->method('getByCurrency') + ->with($currency) + ->willReturn($rulesFromDb); + + $context = $this->createPaymentContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + + $expectedRules = [$this->createPaymentMethodsConfigsRuleMock()]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredPaymentMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getPaymentMethodsConfigsRules($context) + ); + } + + /** + * @return AddressInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createAddressMock() + { + return $this->createMock(AddressInterface::class); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodProviderStub.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodProviderStub.php new file mode 100644 index 000000000..94a3f3889 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodProviderStub.php @@ -0,0 +1,48 @@ +setIdentifier(self::METHOD_IDENTIFIER); + + $this->method = $method; + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethods() + { + return [$this->method->getIdentifier() => $this->method]; + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethod($name) + { + if ($name === $this->method->getIdentifier()) { + return $this->method; + } + return null; + } + + /** + * {@inheritdoc} + */ + public function hasPaymentMethod($name) + { + return $name === $this->method->getIdentifier(); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodStub.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodStub.php new file mode 100644 index 000000000..ad649e6b6 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/Provider/Stub/PaymentMethodStub.php @@ -0,0 +1,150 @@ +identifier; + } + + /** + * @param string $identifier + * @return $this + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + + return $this; + } + + /** + * @return string + */ + public function getLabel() + { + return $this->label ?: $this->identifier . '.label'; + } + + /** + * @param string $label + * @return $this + */ + public function setLabel($label) + { + $this->label = $label; + + return $this; + } + + /** + * @return int + */ + public function getSortOrder() + { + return $this->sortOrder; + } + + /** + * @param int $sortOrder + * @return $this + */ + public function setSortOrder($sortOrder) + { + $this->sortOrder = $sortOrder; + + return $this; + } + + /** + * @return string + */ + public function getOptionsConfigurationFormType() + { + return $this->optionsConfigurationFormType; + } + + /** + * @param string $optionsConfigurationFormType + * @return $this + */ + public function setOptionsConfigurationFormType($optionsConfigurationFormType) + { + $this->optionsConfigurationFormType = $optionsConfigurationFormType; + + return $this; + } + + /** + * @return boolean + */ + public function isEnabled() + { + return $this->isEnabled; + } + + /** + * @param boolean $isEnabled + * @return PaymentMethodStub + */ + public function setEnabled($isEnabled) + { + $this->isEnabled = $isEnabled; + return $this; + } + /** + * @inheritDoc + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param boolean $options + * @return PaymentMethodStub + */ + public function setOptions($options) + { + $this->options = $options; + return $this; + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php b/src/Marello/Bundle/PaymentBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php new file mode 100644 index 000000000..fb36cf77c --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php @@ -0,0 +1,96 @@ +filtrationService = $this->createMock(RuleFiltrationServiceInterface::class); + $this->paymentContextToRuleValuesConverter = $this + ->createMock(PaymentContextToRulesValueConverterInterface::class); + + $this->basicMethodsConfigsRulesFiltrationService = new BasicMethodsConfigsRulesFiltrationService( + $this->filtrationService, + $this->paymentContextToRuleValuesConverter + ); + } + + /** + * {@inheritDoc} + */ + public function testGetFilteredPaymentMethodsConfigsRules() + { + $configRules = [ + $this->createPaymentMethodsConfigsRule(), + $this->createPaymentMethodsConfigsRule(), + ]; + $context = $this->createContextMock(); + $values = [ + 'currency' => 'USD', + ]; + + $this->paymentContextToRuleValuesConverter->expects(static::once()) + ->method('convert') + ->with($context) + ->willReturn($values); + + $expectedConfigRules = [ + $this->createPaymentMethodsConfigsRule(), + $this->createPaymentMethodsConfigsRule(), + ]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredRuleOwners') + ->with($configRules, $values) + ->willReturn($expectedConfigRules); + + static::assertEquals( + $expectedConfigRules, + $this->basicMethodsConfigsRulesFiltrationService->getFilteredPaymentMethodsConfigsRules( + $configRules, + $context + ) + ); + } + + /** + * @return PaymentContextInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createContextMock() + { + return $this->createMock(PaymentContextInterface::class); + } + + /** + * @return PaymentMethodsConfigsRule|\PHPUnit\Framework\MockObject\MockObject + */ + private function createPaymentMethodsConfigsRule() + { + return $this->createMock(PaymentMethodsConfigsRule::class); + } +} diff --git a/src/Marello/Bundle/PaymentBundle/Twig/PaymentMethodExtension.php b/src/Marello/Bundle/PaymentBundle/Twig/PaymentMethodExtension.php new file mode 100755 index 000000000..cea343cb2 --- /dev/null +++ b/src/Marello/Bundle/PaymentBundle/Twig/PaymentMethodExtension.php @@ -0,0 +1,111 @@ +paymentMethodLabelFormatter = $paymentMethodLabelFormatter; + $this->dispatcher = $dispatcher; + $this->checker = $checker; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return static::PAYMENT_METHOD_EXTENSION_NAME; + } + + /** + * @param string $paymentMethodName + * + * @return string Payment Method config template path + */ + public function getPaymentMethodConfigRenderData($paymentMethodName) + { + $event = new PaymentMethodConfigDataEvent($paymentMethodName); + if (!array_key_exists($paymentMethodName, $this->configCache)) { + $this->dispatcher->dispatch(PaymentMethodConfigDataEvent::NAME, $event); + $template = $event->getTemplate(); + if (!$template) { + $template = static::DEFAULT_METHOD_CONFIG_TEMPLATE; + } + $this->configCache[$paymentMethodName] = $template; + } + + return $this->configCache[$paymentMethodName]; + } + + /** + * @param string $methodIdentifier + * + * @return bool + */ + public function isPaymentMethodEnabled($methodIdentifier) + { + return $this->checker->isEnabled($methodIdentifier); + } + + /** + * @return array + */ + public function getFunctions() + { + return [ + new TwigFunction( + 'marello_get_payment_method_label', + [$this->paymentMethodLabelFormatter, 'formatPaymentMethodLabel'] + ), + new TwigFunction( + 'marello_payment_method_config_template', + [$this, 'getPaymentMethodConfigRenderData'] + ), + new TwigFunction( + 'marello_payment_method_enabled', + [$this, 'isPaymentMethodEnabled'] + ) + ]; + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Controller/PaymentTermController.php b/src/Marello/Bundle/PaymentTermBundle/Controller/PaymentTermController.php index c0dc0ef48..5af2532b6 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Controller/PaymentTermController.php +++ b/src/Marello/Bundle/PaymentTermBundle/Controller/PaymentTermController.php @@ -4,20 +4,21 @@ use Marello\Bundle\PaymentTermBundle\Entity\PaymentTerm; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; -class PaymentTermController extends Controller +class PaymentTermController extends AbstractController { /** - * @param Request $request * @return array * - * @Route("/", name="marello_paymentterm_paymentterm_index") - * @Template + * @Route( + * path="/", + * name="marello_paymentterm_paymentterm_index" + * ) + * @Template("MarelloPaymentTermBundle:PaymentTerm:index.html.twig") */ - public function indexAction(Request $request) + public function indexAction() { return [ 'entityClass' => PaymentTerm::class, @@ -25,13 +26,15 @@ public function indexAction(Request $request) } /** - * @param Request $request * @return array|\Symfony\Component\HttpFoundation\RedirectResponse * - * @Route("/create", name="marello_paymentterm_paymentterm_create") + * @Route( + * path="/create", + * name="marello_paymentterm_paymentterm_create" + * ) * @Template("MarelloPaymentTermBundle:PaymentTerm:update.html.twig") */ - public function createAction(Request $request) + public function createAction() { $entity = new PaymentTerm(); @@ -39,14 +42,17 @@ public function createAction(Request $request) } /** - * @param Request $request * @param PaymentTerm $entity * @return array|\Symfony\Component\HttpFoundation\RedirectResponse * - * @Route("/update/{id}", requirements={"id" = "\d+"}, name="marello_paymentterm_paymentterm_update") + * @Route( + * path="/update/{id}", + * requirements={"id" = "\d+"}, + * name="marello_paymentterm_paymentterm_update" + * ) * @Template("MarelloPaymentTermBundle:PaymentTerm:update.html.twig") */ - public function updateAction(Request $request, PaymentTerm $entity) + public function updateAction(PaymentTerm $entity) { return $this->update($entity); } @@ -75,14 +81,17 @@ protected function update(PaymentTerm $entity) } /** - * @param Request $request * @param PaymentTerm $entity * @return array * - * @Route("/view/{id}", requirements={"id" = "\d+"}, name="marello_paymentterm_paymentterm_view") - * @Template + * @Route( + * path="/view/{id}", + * requirements={"id" = "\d+"}, + * name="marello_paymentterm_paymentterm_view" + * ) + * @Template("MarelloPaymentTermBundle:PaymentTerm:view.html.twig") */ - public function viewAction(Request $request, PaymentTerm $entity) + public function viewAction(PaymentTerm $entity) { return [ 'entity' => $entity, diff --git a/src/Marello/Bundle/PaymentTermBundle/Entity/PaymentTermSettings.php b/src/Marello/Bundle/PaymentTermBundle/Entity/PaymentTermSettings.php new file mode 100644 index 000000000..f318823df --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Entity/PaymentTermSettings.php @@ -0,0 +1,98 @@ +labels = new ArrayCollection(); + } + + /** + * @return Collection|LocalizedFallbackValue[] + */ + public function getLabels() + { + return $this->labels; + } + + /** + * @param LocalizedFallbackValue $label + * + * @return PaymentTermSettings + */ + public function addLabel(LocalizedFallbackValue $label) + { + if (!$this->labels->contains($label)) { + $this->labels->add($label); + } + + return $this; + } + + /** + * @param LocalizedFallbackValue $label + * + * @return PaymentTermSettings + */ + public function removeLabel(LocalizedFallbackValue $label) + { + if ($this->labels->contains($label)) { + $this->labels->removeElement($label); + } + + return $this; + } + + /** + * @return ParameterBag + */ + public function getSettingsBag() + { + if (null === $this->settings) { + $this->settings = new ParameterBag([ + self::SETTINGS_FIELD_LABELS => $this->getLabels()->toArray() + ]); + } + + return $this->settings; + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Entity/Repository/PaymentTermSettingsRepository.php b/src/Marello/Bundle/PaymentTermBundle/Entity/Repository/PaymentTermSettingsRepository.php new file mode 100644 index 000000000..38faa7834 --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Entity/Repository/PaymentTermSettingsRepository.php @@ -0,0 +1,38 @@ +aclHelper = $aclHelper; + } + + /** + * @return PaymentTermSettings[] + */ + public function findWithEnabledChannel() + { + $qb = $this->createQueryBuilder('pts'); + + $qb + ->join('pts.channel', 'ch') + ->where('ch.enabled = true') + ->orderBy('pts.id'); + + return $this->aclHelper->apply($qb)->getResult(); + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/EventListener/PaymentTermMethodViewListener.php b/src/Marello/Bundle/PaymentTermBundle/EventListener/PaymentTermMethodViewListener.php new file mode 100644 index 000000000..5f4ee72f1 --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/EventListener/PaymentTermMethodViewListener.php @@ -0,0 +1,46 @@ +paymentTermProvider = $paymentTermProvider; + } + + /** + * @param ApplicablePaymentMethodViewEvent $event + */ + public function onApplicablePaymentMethodView(ApplicablePaymentMethodViewEvent $event) + { + + if (strpos($event->getMethodId(), 'payment_term') !== false) { + $context = $event->getPaymentContext(); + $options = $event->getOptions(); + $paymentTerm = null; + if ($context->getCustomer()) { + $paymentTerm = $this->paymentTermProvider->getCustomerPaymentTerm($context->getCustomer()); + } + if (!$paymentTerm) { + $paymentTerm = $this->paymentTermProvider->getDefaultPaymentTerm(); + } + if ($paymentTerm) { + $options['code'] = $paymentTerm->getCode(); + $options['term'] = $paymentTerm->getTerm(); + $event->setOptions($options); + } + } + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermChoiceType.php b/src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermChoiceType.php index 1b1222a88..605422292 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermChoiceType.php +++ b/src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermChoiceType.php @@ -2,7 +2,6 @@ namespace Marello\Bundle\PaymentTermBundle\Form\Type; -use Marello\Bundle\PaymentTermBundle\Entity\PaymentTerm; use Marello\Bundle\PaymentTermBundle\Provider\PaymentTermProvider; use Oro\Bundle\LocaleBundle\Helper\LocalizationHelper; use Symfony\Component\Form\AbstractType; diff --git a/src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermSettingsType.php b/src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermSettingsType.php new file mode 100644 index 000000000..00fa2f3b3 --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Form/Type/PaymentTermSettingsType.php @@ -0,0 +1,61 @@ +add( + 'labels', + LocalizedFallbackValueCollectionType::class, + [ + 'label' => 'marello.paymentterm.settings.labels.label', + 'required' => true, + 'entry_options' => ['constraints' => [new NotBlank()]], + ] + ); + } + + /** + * @param OptionsResolver $resolver + * + * @throws AccessException + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => PaymentTermSettings::class, + ]); + } + + /** + * @return string + */ + public function getBlockPrefix() + { + return self::BLOCK_PREFIX; + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Integration/PaymentTermChannelType.php b/src/Marello/Bundle/PaymentTermBundle/Integration/PaymentTermChannelType.php new file mode 100644 index 000000000..363952058 --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Integration/PaymentTermChannelType.php @@ -0,0 +1,27 @@ +identifierGenerator = $identifierGenerator; + $this->localizationHelper = $localizationHelper; + $this->integrationIconProvider = $integrationIconProvider; + } + + /** + * @param Channel $channel + * + * @return ManualShippingMethod + */ + public function create(Channel $channel) + { + $id = $this->identifierGenerator->generateIdentifier($channel); + $label = $this->getChannelLabel($channel); + $icon = $this->getIcon($channel); + + return new PaymentTermMethod($id, $label, $icon, $channel->isEnabled()); + } + + /** + * @param Channel $channel + * + * @return string + */ + private function getChannelLabel(Channel $channel) + { + /** @var ManualShippingSettings $transport */ + $transport = $channel->getTransport(); + + return (string) $this->localizationHelper->getLocalizedValue($transport->getLabels()); + } + + /** + * @param Channel $channel + * + * @return string|null + */ + private function getIcon(Channel $channel) + { + return $this->integrationIconProvider->getIcon($channel); + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Method/PaymentTermMethod.php b/src/Marello/Bundle/PaymentTermBundle/Method/PaymentTermMethod.php new file mode 100644 index 000000000..7e65dfd3d --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Method/PaymentTermMethod.php @@ -0,0 +1,100 @@ +identifier = $identifier; + $this->label = $label; + $this->icon = $icon; + $this->enabled = $enabled; + } + + /** + * {@inheritDoc} + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * {@inheritDoc} + */ + public function isEnabled() + { + return $this->enabled; + } + + /** + * {@inheritDoc} + */ + public function getLabel() + { + return $this->label; + } + + /** + * {@inheritDoc} + */ + public function getIcon() + { + return $this->icon; + } + + /** + * {@inheritDoc} + */ + public function getOptionsConfigurationFormType() + { + return HiddenType::class; + } + + /** + * {@inheritDoc} + */ + public function getOptions() + { + return []; + } + + /** + * {@inheritDoc} + */ + public function getSortOrder() + { + return 10; + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Method/Provider/PaymentTermMethodProvider.php b/src/Marello/Bundle/PaymentTermBundle/Method/Provider/PaymentTermMethodProvider.php new file mode 100644 index 000000000..9b4964bcd --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Method/Provider/PaymentTermMethodProvider.php @@ -0,0 +1,21 @@ +container) { + return; + } + + $channel = $this->loadIntegration($manager); + + $this->addMethodConfigToDefaultPaymentRule($manager, $channel); + } + + /** + * @param ObjectManager $manager + * + * @return Channel + */ + private function loadIntegration(ObjectManager $manager) + { + $label = (new LocalizedFallbackValue())->setString('Payment Term'); + + $transport = new PaymentTermSettings(); + $transport->addLabel($label); + + $channel = new Channel(); + $channel->setType(PaymentTermChannelType::TYPE) + ->setName('Payment Term') + ->setEnabled(true) + ->setOrganization($this->getOrganization($manager)) + ->setTransport($transport); + + $manager->persist($channel); + $manager->flush(); + + return $channel; + } + + /** + * @param ObjectManager $manager + * @param Channel $channel + */ + private function addMethodConfigToDefaultPaymentRule(ObjectManager $manager, Channel $channel) + { + $methodConfig = new PaymentMethodConfig(); + $methodConfig->setMethod($this->getIdentifier($channel)); + + $defaultPaymentRule = $this->getReference(CreateDefaultPaymentRule::DEFAULT_RULE_REFERENCE); + $defaultPaymentRule->addMethodConfig($methodConfig); + + $manager->persist($defaultPaymentRule); + $manager->flush(); + } + + /** + * @param ObjectManager $manager + * + * @return Organization|object + */ + private function getOrganization(ObjectManager $manager) + { + if ($this->hasReference(LoadOrganizationAndBusinessUnitData::REFERENCE_DEFAULT_ORGANIZATION)) { + return $this->getReference(LoadOrganizationAndBusinessUnitData::REFERENCE_DEFAULT_ORGANIZATION); + } else { + return $manager + ->getRepository('OroOrganizationBundle:Organization') + ->getFirst(); + } + } + + /** + * @param Channel $channel + * + * @return int|string + */ + private function getIdentifier(Channel $channel) + { + return $this->container + ->get('marello_payment_term.method.identifier_generator.method') + ->generateIdentifier($channel); + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/MarelloPaymentTermBundleInstaller.php b/src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/MarelloPaymentTermBundleInstaller.php index df5ecbfef..2eb2125de 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/MarelloPaymentTermBundleInstaller.php +++ b/src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/MarelloPaymentTermBundleInstaller.php @@ -16,7 +16,7 @@ class MarelloPaymentTermBundleInstaller implements Installation */ public function getMigrationVersion() { - return 'v1_0'; + return 'v1_1'; } /** @@ -27,6 +27,8 @@ public function up(Schema $schema, QueryBag $queries) self::createPaymentTermTable($schema); self::createPaymentTermLabelsTable($schema); self::createForeignKeys($schema); + self::createMarelloPaymentTermTransportLabelTable($schema); + self::addMarelloPaymentTermTransportLabelForeignKeys($schema); } /** @@ -57,7 +59,22 @@ protected static function createPaymentTermLabelsTable(Schema $schema) $table->setPrimaryKey(['paymentterm_id', 'localized_value_id']); } + + /** + * @param Schema $schema + */ + protected static function createMarelloPaymentTermTransportLabelTable(Schema $schema) + { + $table = $schema->createTable('marello_payment_term_trans_lbl'); + + $table->addColumn('transport_id', 'integer', []); + $table->addColumn('localized_value_id', 'integer', []); + $table->setPrimaryKey(['transport_id', 'localized_value_id']); + $table->addIndex(['transport_id'], 'marello_payment_term_trans_label_transport_id', []); + $table->addUniqueIndex(['localized_value_id'], 'marello_payment_term_trans_label_localized_value_id', []); + } + /** * @param Schema $schema */ @@ -77,4 +94,29 @@ protected static function createForeignKeys(Schema $schema) ['onDelete' => 'CASCADE'] ); } + + /** + * @param Schema $schema + * + * @throws SchemaException + */ + protected static function addMarelloPaymentTermTransportLabelForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_term_trans_lbl'); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_fallback_localization_val'), + ['localized_value_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_integration_transport'), + ['transport_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } + } diff --git a/src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/v1_1/MarelloPaymentTermBundle.php b/src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/v1_1/MarelloPaymentTermBundle.php new file mode 100644 index 000000000..96f38fb1a --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Migrations/Schema/v1_1/MarelloPaymentTermBundle.php @@ -0,0 +1,63 @@ +createTable('marello_payment_term_trans_lbl'); + + $table->addColumn('transport_id', 'integer', []); + $table->addColumn('localized_value_id', 'integer', []); + + $table->setPrimaryKey(['transport_id', 'localized_value_id']); + $table->addIndex(['transport_id'], 'marello_payment_term_trans_label_transport_id', []); + $table->addUniqueIndex(['localized_value_id'], 'marello_payment_term_trans_label_localized_value_id', []); + } + + /** + * @param Schema $schema + * + * @throws SchemaException + */ + protected static function addMarelloPaymentTermTransportLabelForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_payment_term_trans_lbl'); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_fallback_localization_val'), + ['localized_value_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_integration_transport'), + ['transport_id'], + ['id'], + ['onDelete' => 'CASCADE', 'onUpdate' => null] + ); + } + +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Provider/PaymentTermProvider.php b/src/Marello/Bundle/PaymentTermBundle/Provider/PaymentTermProvider.php index 63ba1deaf..569fb149d 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Provider/PaymentTermProvider.php +++ b/src/Marello/Bundle/PaymentTermBundle/Provider/PaymentTermProvider.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\PaymentTermBundle\Provider; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\PaymentTermBundle\DependencyInjection\Configuration; use Marello\Bundle\PaymentTermBundle\Entity\PaymentTerm; use Oro\Bundle\ConfigBundle\Config\ConfigManager; diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/config/form.yml b/src/Marello/Bundle/PaymentTermBundle/Resources/config/form.yml index 2d740731b..826baa052 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/config/form.yml +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/config/form.yml @@ -12,6 +12,7 @@ services: marello_payment_term.payment_term.form.handler: class: 'Marello\Bundle\PaymentTermBundle\Form\Handler\PaymentTermFormHandler' + public: true arguments: - '@marello_payment_term.payment_term.form' - '@request_stack' diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/actions.yml b/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/actions.yml index a24046cdf..9cbe5a65e 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/actions.yml +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/actions.yml @@ -23,3 +23,156 @@ operations: '@and': - '@equal': [$.isDeleteAllowed, true] substitute_operation: DELETE + + oro_integration_deactivate: + label: oro.integration.deactivate + preactions: + - '@assign_constant_value': + attribute: $.paymentTermType + value: Marello\Bundle\PaymentTermBundle\Integration\PaymentTermChannelType::TYPE + preconditions: + '@and': + - '@not_equal': [$type, $.paymentTermType] + + oro_integration_delete: + label: oro.integration.delete + preactions: + - '@assign_constant_value': + attribute: $.paymentTermType + value: Marello\Bundle\PaymentTermBundle\Integration\PaymentTermChannelType::TYPE + preconditions: + '@and': + - '@not_equal': [$type, $.paymentTermType] + + marello_payment_term_integration_deactivate: + label: oro.integration.deactivate + extends: oro_integration_deactivate + for_all_entities: false + for_all_datagrids: false + replace: + - preactions + - preconditions + - frontend_options + preactions: + - '@assign_constant_value': + attribute: $.paymentTermType + value: Marello\Bundle\PaymentTermBundle\Integration\PaymentTermChannelType::TYPE + - '@call_service_method': + attribute: $.actionAllowed + service: oro_integration.utils.edit_mode + method: isSwitchEnableAllowed + method_parameters: [$.data.editMode] + - '@call_service_method': + attribute: $.methodIdentifier + service: marello_payment_term.method.identifier_generator.method + method: generateIdentifier + method_parameters: [$.data] + - '@call_service_method': + attribute: $.linkGrid + service: marello_payment.helper.filtered_datagrid_route + method: generate + method_parameters: [{'methodConfigs': $.methodIdentifier}] + preconditions: + '@and': + - '@marello_payment_method_has_enabled_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.paymentTermType] + - '@equal': [$.actionAllowed, true] + - '@equal': [$.data.enabled, true] + frontend_options: + confirmation: + title: marello.payment.integration.deactivate.title + okText: marello.payment.integration.deactivate.button.okText + message: marello.payment.integration.deactivate.message + message_parameters: + linkGrid: $.linkGrid + component: oroui/js/standart-confirmation + + marello_payment_term_integration_deactivate_without_rules: + label: oro.integration.deactivate + extends: marello_payment_term_integration_deactivate + for_all_entities: false + for_all_datagrids: false + replace: + - preconditions + - frontend_options + preconditions: + '@and': + - '@not': + - '@marello_payment_method_has_enabled_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.paymentTermType] + - '@equal': [$.actionAllowed, true] + - '@equal': [$.data.enabled, true] + frontend_options: ~ + + marello_payment_term_integration_delete: + label: oro.integration.delete + extends: oro_integration_delete + for_all_entities: false + for_all_datagrids: false + replace: + - preactions + - preconditions + - frontend_options + preactions: + - '@assign_constant_value': + attribute: $.paymentTermType + value: Marello\Bundle\PaymentTermBundle\Integration\PaymentTermChannelType::TYPE + - '@call_service_method': + service: oro_integration.utils.edit_mode + method: isEditAllowed + method_parameters: [$.data.editMode] + attribute: $.actionAllowed + - '@call_service_method': + attribute: $.methodIdentifier + service: marello_payment_term.method.identifier_generator.method + method: generateIdentifier + method_parameters: [$.data] + - '@call_service_method': + attribute: $.linkGrid + service: marello_payment.helper.filtered_datagrid_route + method: generate + method_parameters: [{'methodConfigs': $.methodIdentifier}] + preconditions: + '@and': + - '@marello_payment_method_has_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.paymentTermType] + - '@equal': [$.actionAllowed, true] + frontend_options: + confirmation: + title: marello.payment.integration.delete.title + okText: marello.payment.integration.delete.button.okText + message: marello.payment.integration.delete.message + message_parameters: + linkGrid: $.linkGrid + component: oroui/js/standart-confirmation + + marello_payment_term_integration_delete_without_rules: + label: oro.integration.delete + extends: marello_payment_term_integration_delete + for_all_entities: false + for_all_datagrids: false + replace: + - preconditions + - frontend_options + preconditions: + '@and': + - '@not': + - '@marello_payment_method_has_payment_rules': + parameters: + method_identifier: $.methodIdentifier + - '@equal': [$type, $.paymentTermType] + - '@equal': [$.actionAllowed, true] + frontend_options: + title: oro.action.delete_entity + confirmation: + title: oro.action.delete_entity + message: oro.action.delete_confirm + message_parameters: + entityLabel: $name + component: oroui/js/delete-confirmation diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/datagrids.yml index 091b503c4..617380eb3 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/config/oro/datagrids.yml @@ -44,7 +44,7 @@ datagrids: term: data_name: term default: - label: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC' + label: 'ASC' properties: id: ~ view_link: diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/config/services.yml b/src/Marello/Bundle/PaymentTermBundle/Resources/config/services.yml index aaf989304..3ba8e354f 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/config/services.yml @@ -1,3 +1,8 @@ +parameters: + marello_payment_term.method.identifier_prefix.payment_term: 'payment_term' + marello_payment_term.integration.channel.type: 'payment_term' + marello_payment_term.integration.transport.type: 'payment_term' + services: marello_payment_term.provider.payment_term: class: 'Marello\Bundle\PaymentTermBundle\Provider\PaymentTermProvider' @@ -7,11 +12,65 @@ services: marello_payment_term.provider.payment_term_delete_permission: class: 'Marello\Bundle\PaymentTermBundle\Provider\PaymentTermDeletePermissionProvider' + public: true arguments: - '@oro_entity.doctrine_helper' - '@marello_payment_term.provider.payment_term' marello_payment_term.action_permissions.payment_term: class: 'Marello\Bundle\PaymentTermBundle\Datagrid\PaymentTermActionPermissionProvider' + public: true arguments: - '@marello_payment_term.provider.payment_term_delete_permission' + + marello_payment_term.integration.channel: + class: 'Marello\Bundle\PaymentTermBundle\Integration\PaymentTermChannelType' + public: true + tags: + - { name: oro_integration.channel, type: payment_term } + + marello_payment_term.integration.transport: + class: 'Marello\Bundle\PaymentTermBundle\Integration\PaymentTermTransport' + public: false + tags: + - { name: oro_integration.transport, type: payment_term, channel_type: payment_term } + + marello_payment_term.method.identifier_generator.method: + parent: oro_integration.generator.prefixed_identifier_generator + public: true + arguments: + - '%marello_payment_term.method.identifier_prefix.payment_term%' + + marello_payment_term.factory.method: + class: 'Marello\Bundle\PaymentTermBundle\Method\Factory\PaymentTermMethodFromChannelFactory' + public: false + arguments: + - '@marello_payment_term.method.identifier_generator.method' + - '@oro_locale.helper.localization' + - '@oro_integration.provider.integration_icon' + + marello_payment_term.payment_method_provider.payment_term: + class: 'Marello\Bundle\PaymentTermBundle\Method\Provider\PaymentTermMethodProvider' + public: false + arguments: + - '%marello_payment_term.integration.channel.type%' + - '@oro_entity.doctrine_helper' + - '@marello_payment_term.factory.method' + tags: + - { name: marello_payment.payment_method_provider } + - { name: doctrine.orm.entity_listener, entity: 'Oro\Bundle\IntegrationBundle\Entity\Channel', event: postLoad } + + marello_payment_term.repository.payment_term_settings: + class: 'Marello\Bundle\PaymentTermBundle\Entity\Repository\PaymentTermSettingsRepository' + parent: oro_entity.abstract_repository + arguments: + - 'Marello\Bundle\PaymentTermBundle\Entity\PaymentTermSettings' + calls: + - [setAclHelper, ['@oro_security.acl_helper']] + + marello_payment_term.event_listener.payment_term_method_view: + class: 'Marello\Bundle\PaymentTermBundle\EventListener\PaymentTermMethodViewListener' + arguments: + - '@marello_payment_term.provider.payment_term' + tags: + - { name: kernel.event_listener, event: marello_payment.applicable_payment_method_view, method: onApplicablePaymentMethodView } diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/public/img/payment-term-logo.png b/src/Marello/Bundle/PaymentTermBundle/Resources/public/img/payment-term-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bed3734827440f6df2af57ea9029d35bff138aaa GIT binary patch literal 4650 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 literal 0 HcmV?d00001 diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/PaymentTermBundle/Resources/translations/messages.en.yml index 3a2550566..281bb9c40 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/translations/messages.en.yml @@ -24,3 +24,9 @@ marello: term.label: "Term" id.label: "ID" labels.label: "Label" + + settings: + label: 'Payment Term Settings' + labels.label: 'Label' + channel_type: + label: 'Payment Term' diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/index.html.twig b/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/index.html.twig index 7c4307858..ee137e6cc 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/index.html.twig +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/index.html.twig @@ -5,7 +5,7 @@ {% set gridName = 'marello-paymentterm-grid' %} {% block navButtons %} - {% if resource_granted('marello_paymentterm_paymentterm_create') %} + {% if is_granted('marello_paymentterm_paymentterm_create') %} {{ UI.addButton({ 'path': path('marello_paymentterm_paymentterm_create'), 'entity_label': 'marello.paymentterm.entity_label'|trans diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/update.html.twig b/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/update.html.twig index 0ed832082..bb4e501ce 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/update.html.twig +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} @@ -12,9 +13,9 @@ {{ parent() }} {{ UI.cancelButton(path('marello_paymentterm_paymentterm_index')) }} - {% if resource_granted('marello_paymentterm_paymentterm_update') %} + {% if is_granted('marello_paymentterm_paymentterm_update') %} {% set html = '' %} - {% if resource_granted('marello_paymentterm_paymentterm_view') %} + {% if is_granted('marello_paymentterm_paymentterm_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_paymentterm_paymentterm_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/view.html.twig b/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/view.html.twig index 3b6e96b68..90315bb5c 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/view.html.twig +++ b/src/Marello/Bundle/PaymentTermBundle/Resources/views/PaymentTerm/view.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:view.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% import 'OroEntityConfigBundle::macros.html.twig' as entityConfig %} diff --git a/src/Marello/Bundle/PaymentTermBundle/Tests/Functional/Controller/SystemConfigurationControllerTest.php b/src/Marello/Bundle/PaymentTermBundle/Tests/Functional/Controller/SystemConfigurationControllerTest.php index 10a76db04..30bc17a43 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Tests/Functional/Controller/SystemConfigurationControllerTest.php +++ b/src/Marello/Bundle/PaymentTermBundle/Tests/Functional/Controller/SystemConfigurationControllerTest.php @@ -2,7 +2,6 @@ namespace Marello\Bundle\PaymenttermBundle\Tests\Functional\Controller; -use Marello\Bundle\PaymentTermBundle\Form\Type\PaymentTermType; use Marello\Bundle\PaymentTermBundle\Tests\Functional\DataFixtures\LoadPaymentTermsData; use Oro\Bundle\TestFrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\Response; @@ -46,7 +45,7 @@ public function testSystemConfiguration() $crawler->filter('select[name="payment_config[marello_payment_term___default_payment_term][value]"]') ); - $form = $crawler->selectbutton('Save settings')->form(); + $form = $crawler->selectButton('Save settings')->form(); $selectField = $form['payment_config[marello_payment_term___default_payment_term][value]']; static::assertEquals( @@ -89,7 +88,7 @@ public function testSystemConfigurationUpdate() $result = $this->client->getResponse(); $this->assertHtmlResponseStatusCodeEquals($result, Response::HTTP_OK); - $form = $crawler->selectbutton('Save settings')->form(); + $form = $crawler->selectButton('Save settings')->form(); $selectField = $form['payment_config[marello_payment_term___default_payment_term][value]']; static::assertEquals( diff --git a/src/Marello/Bundle/PaymentTermBundle/Tests/Functional/DataFixtures/LoadPaymentTermIntegration.php b/src/Marello/Bundle/PaymentTermBundle/Tests/Functional/DataFixtures/LoadPaymentTermIntegration.php new file mode 100644 index 000000000..62632a7a5 --- /dev/null +++ b/src/Marello/Bundle/PaymentTermBundle/Tests/Functional/DataFixtures/LoadPaymentTermIntegration.php @@ -0,0 +1,54 @@ +setString('Payment Term'); + + $transport = new PaymentTermSettings(); + $transport->addLabel($label); + + $channel = new Channel(); + $channel->setType(PaymentTermChannelType::TYPE) + ->setName('Payment Term') + ->setEnabled(true) + ->setTransport($transport) + ->setOrganization($this->getOrganization()); + + $manager->persist($channel); + $manager->flush(); + + $this->setReference(self::REFERENCE_PAYMENT_TERM, $channel); + } + + + /** + * @return Organization + */ + private function getOrganization() + { + return $this->container->get('doctrine') + ->getRepository('OroOrganizationBundle:Organization') + ->getFirst(); + } +} diff --git a/src/Marello/Bundle/PaymentTermBundle/Tests/Unit/Provider/PaymentTermDeletePermissionProviderTest.php b/src/Marello/Bundle/PaymentTermBundle/Tests/Unit/Provider/PaymentTermDeletePermissionProviderTest.php index 652caf152..529d96833 100644 --- a/src/Marello/Bundle/PaymentTermBundle/Tests/Unit/Provider/PaymentTermDeletePermissionProviderTest.php +++ b/src/Marello/Bundle/PaymentTermBundle/Tests/Unit/Provider/PaymentTermDeletePermissionProviderTest.php @@ -38,6 +38,8 @@ public function setUp() } /** + * @param PaymentTerm $paymentTerm + * @param bool $expectedValue * @dataProvider isDeleteAllowedDataProvider */ public function testIsDeleteAllowed(PaymentTerm $paymentTerm, bool $expectedValue) diff --git a/src/Marello/Bundle/PdfBundle/Controller/DownloadController.php b/src/Marello/Bundle/PdfBundle/Controller/DownloadController.php new file mode 100644 index 000000000..0b86fa99f --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Controller/DownloadController.php @@ -0,0 +1,18 @@ +container->get('marello_pdf.request_handler.composite')->handle($request); + } +} diff --git a/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/DocumentTableProviderPass.php b/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/DocumentTableProviderPass.php new file mode 100644 index 000000000..0e671ce1e --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/DocumentTableProviderPass.php @@ -0,0 +1,25 @@ +hasDefinition(self::PROVIDER_SERVICE_ID)) { + $definition = $container->getDefinition(self::PROVIDER_SERVICE_ID); + + $services = $container->findTaggedServiceIds(self::TAG_NAME); + foreach ($services as $serviceId => $tags) { + $definition->addMethodCall('addProvider', [new Reference($serviceId)]); + } + } + } +} diff --git a/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RenderParameterProviderPass.php b/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RenderParameterProviderPass.php new file mode 100644 index 000000000..22476e3f3 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RenderParameterProviderPass.php @@ -0,0 +1,25 @@ +hasDefinition(self::PROVIDER_SERVICE_ID)) { + $definition = $container->getDefinition(self::PROVIDER_SERVICE_ID); + + $services = $container->findTaggedServiceIds(self::TAG_NAME); + foreach ($services as $serviceId => $tags) { + $definition->addMethodCall('addProvider', [new Reference($serviceId)]); + } + } + } +} diff --git a/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RequestHandlersPass.php b/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RequestHandlersPass.php new file mode 100644 index 000000000..859fb59a0 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/DependencyInjection/CompilerPass/RequestHandlersPass.php @@ -0,0 +1,25 @@ +hasDefinition(self::PROVIDER_SERVICE_ID)) { + $definition = $container->getDefinition(self::PROVIDER_SERVICE_ID); + + $services = $container->findTaggedServiceIds(self::TAG_NAME); + foreach ($services as $serviceId => $tags) { + $definition->addMethodCall('addHandler', [new Reference($serviceId)]); + } + } + } +} diff --git a/src/Marello/Bundle/PdfBundle/DependencyInjection/Configuration.php b/src/Marello/Bundle/PdfBundle/DependencyInjection/Configuration.php new file mode 100644 index 000000000..06d825b73 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/DependencyInjection/Configuration.php @@ -0,0 +1,80 @@ +root(self::CONFIG_NAME); + + SettingsBuilder::append( + $rootNode, + [ + self::CONFIG_KEY_PAPER_SIZE => ['value' => self::PAPER_SIZE_A4], + self::CONFIG_KEY_LOCALIZATION => ['value' => null], + self::CONFIG_KEY_COMPANY_ADDRESS => ['value' => null], + self::CONFIG_KEY_COMPANY_EMAIL => ['value' => null], + self::CONFIG_KEY_LOGO => ['value' => null], + self::CONFIG_KEY_COMPANY_PHONE => ['value' => null], + self::CONFIG_KEY_COMPANY_BANK => ['value' => null], + self::CONFIG_KEY_COMPANY_COC => ['value' => null], + self::CONFIG_KEY_EMAIL_WORKFLOW_TRANSITION => ['value' => null], + self::CONFIG_KEY_EMAIL_SENDER_NAME => ['value' => null], + self::CONFIG_KEY_EMAIL_SENDER_EMAIL => ['value' => null], + self::CONFIG_KEY_EMAIL_BCC => ['value' => null], + ] + ); + + return $treeBuilder; + } + + /** + * @param string $name + * @return string + */ + public static function getConfigKeyByName(string $name): string + { + return TreeUtils::getConfigKey(self::CONFIG_NAME, $name, ConfigManager::SECTION_MODEL_SEPARATOR); + } + + /** + * @param string $name + * @return string + */ + public static function getFieldKeyByName(string $name): string + { + return TreeUtils::getConfigKey(self::CONFIG_NAME, $name, ConfigManager::SECTION_VIEW_SEPARATOR); + } +} diff --git a/src/Marello/Bundle/PdfBundle/DependencyInjection/MarelloPdfExtension.php b/src/Marello/Bundle/PdfBundle/DependencyInjection/MarelloPdfExtension.php new file mode 100644 index 000000000..424d1868c --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/DependencyInjection/MarelloPdfExtension.php @@ -0,0 +1,30 @@ +processConfiguration($configuration, $configs); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + + $container->prependExtensionConfig(Configuration::CONFIG_NAME, $config); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Exception/PaperSizeNotSetException.php b/src/Marello/Bundle/PdfBundle/Exception/PaperSizeNotSetException.php new file mode 100644 index 000000000..ba09e65b4 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Exception/PaperSizeNotSetException.php @@ -0,0 +1,11 @@ +passedOptions = $options; + } + + public function create(array $options = []) + { + return new Mpdf($this->getOptions($options)); + } + + protected function getOptions(array $options = []) + { + if ($this->options === null) { + $this->options = array_merge($this->defaultOptions, $this->passedOptions); + } + + return array_merge($this->options, $options); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Form/Configurator/DocumentConfigurator.php b/src/Marello/Bundle/PdfBundle/Form/Configurator/DocumentConfigurator.php new file mode 100644 index 000000000..9e1f8f84f --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Form/Configurator/DocumentConfigurator.php @@ -0,0 +1,77 @@ +configHandler = $configHandler; + } + + /** + * @param FormBuilderInterface $builder + */ + public function buildForm(FormBuilderInterface $builder): void + { + $builder->addEventListener( + FormEvents::PRE_SET_DATA, + function (FormEvent $event) { + if ($event->getData() === null) { + return; + } + + $form = $event->getForm(); + $data = $event->getData(); + $configManager = $this->configHandler->getConfigManager(); + if ($data[PdfConfig::getFieldKeyByName(PdfConfig::CONFIG_KEY_LOCALIZATION)]['value'] === null) { + $data[PdfConfig::getFieldKeyByName(PdfConfig::CONFIG_KEY_LOCALIZATION)]['value'] = + $configManager->get(LocaleConfig::getConfigKeyByName(LocaleConfig::DEFAULT_LOCALIZATION)); + $event->setData($data); + } + + $this->setEnabledLocalizations($form, $configManager); + } + ); + } + + /** + * @param FormInterface $form + * @param ConfigManager $configManager + */ + private function setEnabledLocalizations(FormInterface $form, ConfigManager $configManager): void + { + $form = $form->get(PdfConfig::getFieldKeyByName(PdfConfig::CONFIG_KEY_LOCALIZATION)); + + $options = $form->get('value') + ->getConfig() + ->getOptions(); + + $options[LocaleConfig::ENABLED_LOCALIZATIONS] = $configManager->get( + LocaleConfig::getConfigKeyByName(LocaleConfig::ENABLED_LOCALIZATIONS) + ); + + $form->add('value', LocalizationSelectionType::class, $options); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Form/Type/WorkflowTransitionSelectType.php b/src/Marello/Bundle/PdfBundle/Form/Type/WorkflowTransitionSelectType.php new file mode 100644 index 000000000..9e5f21f97 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Form/Type/WorkflowTransitionSelectType.php @@ -0,0 +1,59 @@ +workflowManager = $workflowManager; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setRequired('workflow') + ->setDefault('choices', function (Options $options) { + return $this->getWorkflowStepChoices($options['workflow']); + }) + ->setDefaults([ + 'required' => false, + ]) + ; + } + + protected function getWorkflowStepChoices($workflowName) + { + $workflow = $this->getWorkflow($workflowName); + $configuration = $workflow->getDefinition()->getConfiguration(); + + $choices = []; + foreach (array_keys($configuration['transitions']) as $transition) { + $choices[$this->formatTransitionName($transition)] = $transition; + } + + return $choices; + } + + protected function formatTransitionName($transition) + { + return ucfirst(str_replace('_', ' ', $transition)); + } + + protected function getWorkflow($workflowName) + { + return $this->workflowManager->getWorkflow($workflowName); + } + + public function getParent() + { + return ChoiceType::class; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Lib/View/EmptyDisplayLine.php b/src/Marello/Bundle/PdfBundle/Lib/View/EmptyDisplayLine.php new file mode 100644 index 000000000..922236697 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Lib/View/EmptyDisplayLine.php @@ -0,0 +1,26 @@ +fields[$field] = [null]; + } + } + + public function offsetExists($key) + { + return isset($this->fields[$key]) || array_key_exists($key, $this->fields); + } + + public function offsetGet($key) + { + if ($this->offsetExists($key) === false) { + throw $this->createInvalidArgumentException($key); + } + + return $this->fields[$key]; + } + + public function offsetSet($key, $value) + { + if ($this->offsetExists($key) === false) { + throw $this->createInvalidArgumentException($key); + } + + if (!is_array($value)) { + $value = [$value]; + } + + $this->fields[$key] = $value; + $this->recalculateHeight(); + } + + public function offsetUnset($key) + { + if ($this->offsetExists($key) === false) { + throw $this->createInvalidArgumentException($key); + } + + $this->fields[$key] = [null]; + } + + public function getHeight() + { + return $this->height; + } + + public function getDisplayLines() + { + $lines = []; + for ($i = 0; $i < $this->height; $i++) { + $line = []; + + foreach (array_keys($this->fields) as $key) { + $line[$key] = $this->fields[$key][$i] ?? null; + } + + $lines[] = $line; + } + + return $lines; + } + + public function isEmpty() + { + foreach ($this->fields as $field) { + if (count($field) > 1 || reset($field) !== null) { + return false; + } + } + + return true; + } + + protected function recalculateHeight() + { + $this->height = max(array_map('count', $this->fields)); + } + + private function createInvalidArgumentException($key) + { + return new \InvalidArgumentException(sprintf( + 'Key "%s" does not exist in %s object with keys %s', + $key, + __CLASS__, + implode(', ', array_keys($this->fields)) + )); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Lib/View/Table.php b/src/Marello/Bundle/PdfBundle/Lib/View/Table.php new file mode 100644 index 000000000..4fc28c6a2 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Lib/View/Table.php @@ -0,0 +1,90 @@ +maxHeight = $maxHeight; + $this->headerHeight = $headerHeight; + $this->footerHeight = $footerHeight; + $this->lines = new ArrayCollection(); + } + + public function disableHeader() + { + $this->headerEnabled = false; + + return $this; + } + + public function disableFooter() + { + $this->footerEnabled = false; + + return $this; + } + + public function getLines() + { + $lines = clone $this->lines; + + for ($i = $this->getHeight(); $i < $this->getMaxHeight(); $i++) { + $lines[] = new EmptyLine(); + } + + return $lines; + } + + public function addLine(Line $line) + { + $this->lines[] = $line; + + $this->height += $line->getHeight(); + + return $this; + } + + public function getHeight() + { + return $this->height; + } + + public function getMaxHeight() + { + $height = $this->maxHeight; + if ($this->headerEnabled) { + $height -= $this->headerHeight; + } + if ($this->footerEnabled) { + $height -= $this->footerHeight; + } + + return $height; + } + + public function fitsLine(Line $line) + { + $height = $this->getHeight(); + $height += $line->getHeight(); + + return $height <= $this->getMaxHeight(); + } +} diff --git a/src/Marello/Bundle/PdfBundle/MarelloPdfBundle.php b/src/Marello/Bundle/PdfBundle/MarelloPdfBundle.php new file mode 100644 index 000000000..0bbae1bf8 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/MarelloPdfBundle.php @@ -0,0 +1,21 @@ +addCompilerPass(new RenderParameterProviderPass()); + $container->addCompilerPass(new DocumentTableProviderPass()); + $container->addCompilerPass(new RequestHandlersPass()); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Provider/DocumentTableProvider.php b/src/Marello/Bundle/PdfBundle/Provider/DocumentTableProvider.php new file mode 100644 index 000000000..4f2150097 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Provider/DocumentTableProvider.php @@ -0,0 +1,24 @@ +providers[] = $provider; + } + + public function getTables($entity) + { + foreach ($this->providers as $provider) { + if ($provider->supports($entity)) { + return $provider->getTables($entity); + } + } + + return []; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Provider/Render/ConfigValuesProvider.php b/src/Marello/Bundle/PdfBundle/Provider/Render/ConfigValuesProvider.php new file mode 100644 index 000000000..4480076e9 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Provider/Render/ConfigValuesProvider.php @@ -0,0 +1,43 @@ +config = $config; + $this->parameters = $parameters; + } + + public function supports($entity, array $options) + { + return true; + } + + public function getParams($entity, array $options) + { + $params = []; + foreach ($this->parameters as $key => $configKey) { + $params[$key] = $this->getConfigValue($configKey, $entity, $options); + } + + return $params; + } + + protected function getConfigValue($configKey, $entity, array $options) + { + $scopeIdentifier = $options[self::SCOPE_IDENTIFIER_KEY] ?? null; + + return $this->config->get($configKey, false, false, $scopeIdentifier); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Provider/Render/EntityProvider.php b/src/Marello/Bundle/PdfBundle/Provider/Render/EntityProvider.php new file mode 100644 index 000000000..15af87c77 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Provider/Render/EntityProvider.php @@ -0,0 +1,18 @@ + $entity]; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Provider/Render/LocalizationProvider.php b/src/Marello/Bundle/PdfBundle/Provider/Render/LocalizationProvider.php new file mode 100644 index 000000000..eb1430e55 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Provider/Render/LocalizationProvider.php @@ -0,0 +1,118 @@ +config = $config; + $this->doctrine = $doctrine; + $this->localizationParameterName = $localizationParameterName; + } + + /** + * {@inheritdoc} + * @param $entity + * @param array $options + * @return bool + */ + public function supports($entity, array $options) + { + return true; + } + + /** + * {@inheritdoc} + * @param $entity + * @param array $options + * @return array + */ + public function getParams($entity, array $options) + { + $params = []; + $localizationId = (int)$this->getConfigValue($this->localizationParameterName, $options); + $localization = $this->doctrine + ->getManagerForClass(Localization::class) + ->find(Localization::class, $localizationId); + + if (null === $localization) { + $localizationAwareEntity = null; + if ($entity instanceof LocalizationAwareInterface) { + $localizationAwareEntity = $entity; + } elseif ($entity instanceof SalesChannelAwareInterface) { + $localizationAwareEntity = $entity->getSalesChannel(); + } + + if ($this->chainLocalizationProvider && null !== $localizationAwareEntity) { + $localization = $this->chainLocalizationProvider->getLocalization($localizationAwareEntity); + } + } + + if ($localization) { + $params['localization'] = $localization; + $params['language'] = $localization->getLanguageCode(); + $params['locale'] = $localization->getLanguageCode(); + } + + return $params; + } + + /** + * {@inheritdoc} + * @param $configKey + * @param array $options + * @return mixed + */ + protected function getConfigValue($configKey, array $options) + { + $scopeIdentifier = $options[self::SCOPE_IDENTIFIER_KEY] ?? null; + + return $this->config->get($configKey, false, false, $scopeIdentifier); + } + + /** + * {@inheritdoc} + * @param EntityLocalizationProviderInterface $chainProvider + */ + public function setChainLocalizationProvider(EntityLocalizationProviderInterface $chainProvider) + { + $this->chainLocalizationProvider = $chainProvider; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Provider/RenderParameterProviderInterface.php b/src/Marello/Bundle/PdfBundle/Provider/RenderParameterProviderInterface.php new file mode 100644 index 000000000..7fd67e2b9 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Provider/RenderParameterProviderInterface.php @@ -0,0 +1,9 @@ +providers[] = $provider; + } + + public function getParams($entity, array $options = []) + { + $params = []; + + foreach ($this->providers as $provider) { + if ($provider->supports($entity, $options)) { + $params = array_merge($params, $provider->getParams($entity, $options)); + } + } + + return $params; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Provider/TableProviderInterface.php b/src/Marello/Bundle/PdfBundle/Provider/TableProviderInterface.php new file mode 100644 index 000000000..64f7d6d20 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Provider/TableProviderInterface.php @@ -0,0 +1,10 @@ +config = $config; + $this->sizeConfig = [ + self::KEY_MAX_HEIGHT => $maxHeight, + self::KEY_MAX_TEXT_WIDTH => $maxTextWidth, + self::KEY_FIRST_PAGE_INFO => $firstPageInfoHeight, + self::KEY_LAST_PAGE_INFO => $lastPageInfoHeight, + ]; + } + + public function getMaxHeight(SalesChannel $salesChannel) + { + return $this->getValue($salesChannel, self::KEY_MAX_HEIGHT); + } + + public function getMaxTextWidth(SalesChannel $salesChannel) + { + return $this->getValue($salesChannel, self::KEY_MAX_TEXT_WIDTH); + } + + public function getFirstPageInfoHeight(SalesChannel $salesChannel) + { + return $this->getValue($salesChannel, self::KEY_FIRST_PAGE_INFO); + } + + public function getLastPageInfoHeight(SalesChannel $salesChannel) + { + return $this->getValue($salesChannel, self::KEY_LAST_PAGE_INFO); + } + + protected function getValue(SalesChannel $salesChannel, $key) + { + $compoundValue = $this->sizeConfig[$key]; + if (!is_array($compoundValue)) { + return $compoundValue; + } + + if (!isset($compoundValue[$this->getPaperSize($salesChannel)])) { + throw new PaperSizeNotSetException($this->getPaperSize($salesChannel), $key); + } + + return $compoundValue[$this->getPaperSize($salesChannel)]; + } + + private function getPaperSize(SalesChannel $salesChannel) + { + if (!isset($this->paperSizes[$salesChannel->getId()])) { + $this->paperSizes[$salesChannel->getId()] = $this->fetchPaperSize($salesChannel); + } + + return $this->paperSizes[$salesChannel->getId()]; + } + + private function fetchPaperSize(SalesChannel $salesChannel) + { + return $this->getConfigValue(Configuration::CONFIG_KEY_PAPER_SIZE, $salesChannel); + } + + private function getConfigValue($configKey, SalesChannel $salesChannel) + { + $key = sprintf('%s.%s', Configuration::CONFIG_NAME, $configKey); + + return $this->config->get($key, false, false, $salesChannel); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Renderer/HtmlRenderer.php b/src/Marello/Bundle/PdfBundle/Renderer/HtmlRenderer.php new file mode 100644 index 000000000..117611cd6 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Renderer/HtmlRenderer.php @@ -0,0 +1,39 @@ +pdfWriterFactory = $pdfWriterFactory; + } + + public function render($input) + { + $writer = $this->getWriter(); + + $writer->WriteHTML($input); + + return $writer->Output(null, Destination::STRING_RETURN); + } + + public function renderToFile($input, $filename) + { + $writer = $this->getWriter(); + + $writer->WriteHTML($input); + + return $writer->Output($filename, Destination::FILE); + } + + protected function getWriter() + { + return $this->pdfWriterFactory->create(); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Renderer/TwigRenderer.php b/src/Marello/Bundle/PdfBundle/Renderer/TwigRenderer.php new file mode 100644 index 000000000..a2c3a2587 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Renderer/TwigRenderer.php @@ -0,0 +1,38 @@ +renderer = $renderer; + $this->twig = $twig; + } + + public function render($template, array $params = []) + { + return $this->renderer->render($this->renderTwig($template, $params)); + } + + public function renderToFile($template, array $params, $filename) + { + return $this->renderer->renderToFile($this->renderTwig($template, $params), $filename); + } + + public function renderBase64($template, array $params = []) + { + return base64_encode($this->render($template, $params)); + } + + protected function renderTwig($template, array $params = []) + { + return $this->twig->render($template, $params); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Request/CompositePdfRequestHandler.php b/src/Marello/Bundle/PdfBundle/Request/CompositePdfRequestHandler.php new file mode 100644 index 000000000..0d7065ee8 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Request/CompositePdfRequestHandler.php @@ -0,0 +1,43 @@ +handlers[] = $handler; + } + + /** + * @inheritDoc + */ + public function isApplicable(Request $request) + { + return true; + } + + /** + * @inheritDoc + */ + public function handle(Request $request) + { + foreach ($this->handlers as $handler) { + if ($handler->isApplicable($request)) { + return $handler->handle($request); + } + } + + return null; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Request/PdfRequestHandlerInterface.php b/src/Marello/Bundle/PdfBundle/Request/PdfRequestHandlerInterface.php new file mode 100644 index 000000000..f25aa35b1 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Request/PdfRequestHandlerInterface.php @@ -0,0 +1,21 @@ + + +
+{% endblock header %} + +{% block footer %} + +{% endblock footer %} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/DocumentTableProviderPassTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/DocumentTableProviderPassTest.php new file mode 100644 index 000000000..6ec600a80 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/DocumentTableProviderPassTest.php @@ -0,0 +1,122 @@ +containerBuilder = $this + ->getMockBuilder(ContainerBuilder::class) + ->getMock(); + + $this->compilerPass = new DocumentTableProviderPass(); + } + + public function testProcessRegistryDoesNotExist() + { + $this->containerBuilder + ->expects($this->once()) + ->method('hasDefinition') + ->with(DocumentTableProviderPass::PROVIDER_SERVICE_ID) + ->willReturn(false) + ; + + $this->containerBuilder + ->expects($this->never()) + ->method('getDefinition') + ; + + $this->containerBuilder + ->expects($this->never()) + ->method('findTaggedServiceIds') + ; + + $this->compilerPass->process($this->containerBuilder); + } + + public function testProcessNoTaggedServicesFound() + { + $this->containerBuilder + ->expects($this->once()) + ->method('hasDefinition') + ->with(DocumentTableProviderPass::PROVIDER_SERVICE_ID) + ->willReturn(true) + ; + + $this->containerBuilder + ->expects($this->once()) + ->method('findTaggedServiceIds') + ->with(DocumentTableProviderPass::TAG_NAME) + ->willReturn([]) + ; + + $this->containerBuilder + ->expects($this->once()) + ->method('getDefinition') + ->with(DocumentTableProviderPass::PROVIDER_SERVICE_ID) + ; + + $this->compilerPass->process($this->containerBuilder); + } + + public function testProcessWithTaggedServices() + { + $this->containerBuilder + ->expects($this->once()) + ->method('hasDefinition') + ->with(DocumentTableProviderPass::PROVIDER_SERVICE_ID) + ->willReturn(true); + + $registryServiceDefinition = $this->createMock(Definition::class); + + $this->containerBuilder + ->expects($this->once()) + ->method('getDefinition') + ->with(DocumentTableProviderPass::PROVIDER_SERVICE_ID) + ->willReturn($registryServiceDefinition); + + $taggedServices = [ + 'service.name.1' => [[]], + 'service.name.2' => [[]], + 'service.name.3' => [[]], + 'service.name.4' => [[]], + ]; + + $this->containerBuilder + ->expects($this->once()) + ->method('findTaggedServiceIds') + ->with(DocumentTableProviderPass::TAG_NAME) + ->willReturn($taggedServices); + + $registryServiceDefinition + ->expects($this->exactly(4)) + ->method('addMethodCall') + ->withConsecutive( + ['addProvider', [new Reference('service.name.1')]], + ['addProvider', [new Reference('service.name.2')]], + ['addProvider', [new Reference('service.name.3')]], + ['addProvider', [new Reference('service.name.4')]] + ); + + $this->compilerPass->process($this->containerBuilder); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/RenderParameterProviderPassTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/RenderParameterProviderPassTest.php new file mode 100644 index 000000000..9c324aa3b --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/CompilerPass/RenderParameterProviderPassTest.php @@ -0,0 +1,122 @@ +containerBuilder = $this + ->getMockBuilder(ContainerBuilder::class) + ->getMock(); + + $this->compilerPass = new RenderParameterProviderPass(); + } + + public function testProcessRegistryDoesNotExist() + { + $this->containerBuilder + ->expects($this->once()) + ->method('hasDefinition') + ->with(RenderParameterProviderPass::PROVIDER_SERVICE_ID) + ->willReturn(false) + ; + + $this->containerBuilder + ->expects($this->never()) + ->method('getDefinition') + ; + + $this->containerBuilder + ->expects($this->never()) + ->method('findTaggedServiceIds') + ; + + $this->compilerPass->process($this->containerBuilder); + } + + public function testProcessNoTaggedServicesFound() + { + $this->containerBuilder + ->expects($this->once()) + ->method('hasDefinition') + ->with(RenderParameterProviderPass::PROVIDER_SERVICE_ID) + ->willReturn(true) + ; + + $this->containerBuilder + ->expects($this->once()) + ->method('findTaggedServiceIds') + ->with(RenderParameterProviderPass::TAG_NAME) + ->willReturn([]) + ; + + $this->containerBuilder + ->expects($this->once()) + ->method('getDefinition') + ->with(RenderParameterProviderPass::PROVIDER_SERVICE_ID) + ; + + $this->compilerPass->process($this->containerBuilder); + } + + public function testProcessWithTaggedServices() + { + $this->containerBuilder + ->expects($this->once()) + ->method('hasDefinition') + ->with(RenderParameterProviderPass::PROVIDER_SERVICE_ID) + ->willReturn(true); + + $registryServiceDefinition = $this->createMock(Definition::class); + + $this->containerBuilder + ->expects($this->once()) + ->method('getDefinition') + ->with(RenderParameterProviderPass::PROVIDER_SERVICE_ID) + ->willReturn($registryServiceDefinition); + + $taggedServices = [ + 'service.name.1' => [[]], + 'service.name.2' => [[]], + 'service.name.3' => [[]], + 'service.name.4' => [[]], + ]; + + $this->containerBuilder + ->expects($this->once()) + ->method('findTaggedServiceIds') + ->with(RenderParameterProviderPass::TAG_NAME) + ->willReturn($taggedServices); + + $registryServiceDefinition + ->expects($this->exactly(4)) + ->method('addMethodCall') + ->withConsecutive( + ['addProvider', [new Reference('service.name.1')]], + ['addProvider', [new Reference('service.name.2')]], + ['addProvider', [new Reference('service.name.3')]], + ['addProvider', [new Reference('service.name.4')]] + ); + + $this->compilerPass->process($this->containerBuilder); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/MarelloPdfExtensionTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/MarelloPdfExtensionTest.php new file mode 100644 index 000000000..bfc738291 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/DependencyInjection/MarelloPdfExtensionTest.php @@ -0,0 +1,40 @@ +extension = new MarelloPdfExtension(); + } + + protected function tearDown() + { + unset($this->extension); + } + + public function testLoad() + { + $this->loadExtension($this->extension); + + $this->assertDefinitionsLoaded([ + 'marello_pdf.factory.pdf_writer', + 'marello_pdf.renderer.html', + 'marello_pdf.renderer.twig', + 'marello_pdf.provider.render_parameters', + 'marello_pdf.param_provider.entity', + 'marello_pdf.param_provider.saleschannel_config_values', + 'marello_pdf.provider.document_table', + 'marello_pdf.twig_extension.document_table', + ]); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Factory/PdfWriterFactoryTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Factory/PdfWriterFactoryTest.php new file mode 100644 index 000000000..d3338da87 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Factory/PdfWriterFactoryTest.php @@ -0,0 +1,24 @@ +factory = new PdfWriterFactory(); + } + + public function testCreate() + { + $writer = $this->factory->create(); + + $this->assertInstanceOf(Mpdf::class, $writer); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyDisplayLineTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyDisplayLineTest.php new file mode 100644 index 000000000..5ac626fd6 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyDisplayLineTest.php @@ -0,0 +1,40 @@ +line = new EmptyDisplayLine(); + } + + public function testOffsetGet() + { + $this->assertNull($this->line['not set value']); + } + + public function testOffsetExists() + { + $this->assertTrue(isset($this->line['not set value'])); + } + + public function testOffsetSet() + { + $this->expectException(\BadMethodCallException::class); + + $this->line['not set value'] = 'set value'; + } + + public function testOffsetUnset() + { + $this->expectException(\BadMethodCallException::class); + + unset($this->line['set value']); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyLineTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyLineTest.php new file mode 100644 index 000000000..4af5c0280 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/EmptyLineTest.php @@ -0,0 +1,66 @@ +line = new EmptyLine(); + } + + public function testOffsetSet() + { + $this->expectException(\BadMethodCallException::class); + + $this->line['not set value'] = 'set value'; + } + + public function testOffsetGet() + { + $value = $this->line['not set value']; + + $this->assertTrue(is_array($value)); + $this->assertCount(1, $value); + $this->assertNull(reset($value)); + } + + public function testOffsetUnset() + { + $this->expectException(\BadMethodCallException::class); + + unset($this->line['not set value']); + } + + public function testOffsetExists() + { + $this->assertTrue(isset($this->line['not set value'])); + } + + public function testGetDisplayLines() + { + $displayLines = $this->line->getDisplayLines(); + + $this->assertTrue(is_array($displayLines)); + $this->assertCount(1, $displayLines); + foreach ($displayLines as $line) { + $this->assertInstanceOf(EmptyDisplayLine::class, $line); + } + } + + public function testGetHeight() + { + $this->assertEquals(1, $this->line->getHeight()); + } + + public function testIsEmpty() + { + $this->assertTrue($this->line->isEmpty()); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/LineTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/LineTest.php new file mode 100644 index 000000000..ce2da5285 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/LineTest.php @@ -0,0 +1,191 @@ +line = new Line([ + 'test-field-1', + 'test-field-2', + ]); + } + + public function testOffsetExists() + { + $this->line['test-field-1'] = ['test value 1']; + + $this->assertTrue(isset($this->line['test-field-1'])); + $this->assertTrue(isset($this->line['test-field-2'])); + $this->assertFalse(isset($this->line['test-field-3'])); + } + + public function testOffsetGet() + { + $this->line['test-field-1'] = ['test value 1']; + + $this->assertEquals(['test value 1'], $this->line['test-field-1']); + $this->assertEquals([null], $this->line['test-field-2']); + + $this->expectException(\InvalidArgumentException::class); + $this->line['test-field-3']; + } + + public function testOffsetSet() + { + $this->line['test-field-1'] = ['test value 1']; + + $this->assertEquals(['test value 1'], $this->line['test-field-1']); + + $this->expectException(\InvalidArgumentException::class); + $this->line['test-field-3'] = ['test value 3']; + } + + public function testOffsetUnset() + { + $this->line['test-field-1'] = ['test value 1']; + + unset($this->line['test-field-1']); + + $this->assertEquals([null], $this->line['test-field-1']); + + $this->expectException(\InvalidArgumentException::class); + unset($this->line['test-field-3']); + } + + /** + * @param $fieldValues + * @param $returnValue + * + * @dataProvider getDisplayLinesProvider + */ + public function testGetDisplayLines($fieldValues, $returnValue) + { + foreach ($fieldValues as $key => $value) { + $this->line[$key] = $value; + } + + $displayLines = $this->line->getDisplayLines(); + $this->assertTrue(is_array($displayLines)); + $this->assertCount(count($returnValue), $displayLines); + + foreach ($returnValue as $i => $lineValue) { + foreach ($lineValue as $key => $fieldValue) { + $this->assertEquals($fieldValue, $displayLines[$i][$key]); + } + } + } + + /** + * @return array + */ + public function getDisplayLinesProvider() + { + return [ + 'empty' => [ + 'fieldValues' => [], + 'returnValue' => [ + ['test-field-1' => null, 'test-field-2' => null], + ], + ], + 'single' => [ + 'fieldValues' => [ + 'test-field-1' => ['test value 1'], + ], + 'returnValue' => [ + ['test-field-1' => 'test value 1', 'test-field-2' => null], + ], + ], + 'multiple' => [ + 'fieldValues' => [ + 'test-field-1' => ['test value line 1', 'test value line 2'], + ], + 'returnValue' => [ + ['test-field-1' => 'test value line 1', 'test-field-2' => null], + ['test-field-1' => 'test value line 2', 'test-field-2' => null], + ], + ], + ]; + } + + /** + * @param $fieldValues + * @param $height + * + * @dataProvider getHeightProvider + */ + public function testGetHeight($fieldValues, $height) + { + foreach ($fieldValues as $field => $value) { + $this->line[$field] = $value; + } + + $this->assertEquals($height, $this->line->getHeight()); + } + + /** + * @return array + */ + public function getHeightProvider() + { + return [ + 'empty' => [ + 'fieldValues' => [], + 'height' => 1, + ], + 'single' => [ + 'fieldValues' => [ + 'test-field-1' => ['test line 1'], + ], + 'height' => 1, + ], + 'multiple' => [ + 'fieldValues' => [ + 'test-field-1' => ['test line 1', 'test line 2'], + ], + 'height' => 2, + ], + ]; + } + + /** + * @param $fieldValues + * @param $isEmpty + * + * @dataProvider isEmptyProvider + */ + public function testIsEmpty($fieldValues, $isEmpty) + { + foreach ($fieldValues as $field => $value) { + $this->line[$field] = $value; + } + + $this->assertEquals($isEmpty, $this->line->isEmpty()); + } + + /** + * @return array + */ + public function isEmptyProvider() + { + return [ + 'empty' => [ + 'fieldValues' => [], + 'isEmpty' => true, + ], + 'not empty' => [ + 'fieldValues' => [ + 'test-field-1' => 'Value 1', + 'test-field-2' => 'Value 2', + ], + 'isEmpty' => false, + ], + ]; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/TableTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/TableTest.php new file mode 100644 index 000000000..1d7bbc518 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Lib/View/TableTest.php @@ -0,0 +1,178 @@ +table = new Table( + self::MAX_HEIGHT, + self::HEADER_HEIGHT, + self::FOOTER_HEIGHT + ); + } + + public function testDisableHeader() + { + $this->table->disableHeader(); + + $this->assertEquals(self::MAX_HEIGHT - self::FOOTER_HEIGHT, $this->table->getMaxHeight()); + } + + public function testDisableFooter() + { + $this->table->disableFooter(); + + $this->assertEquals(self::MAX_HEIGHT - self::HEADER_HEIGHT, $this->table->getMaxHeight()); + } + + /** + * @param $lines + * + * @dataProvider addLineProvider + */ + public function testAddLine($lines) + { + $line = new Line(['test-field-1']); + $line['test-field-1'] = $lines; + + $this->table->addLine($line); + + $tableLines = $this->table->getLines()->filter(function ($x) { return $x->isEmpty() === false; }); + + $this->assertCount(1, $tableLines); + $this->assertEquals($line, $tableLines->first()); + } + + /** + * @param $lines + * + * @dataProvider addLineProvider + */ + public function testGetHeight($lines) + { + $height = $this->table->getHeight(); + + $line = new Line(['test-field-1']); + $line['test-field-1'] = $lines; + + $this->table->addLine($line); + + $this->assertEquals($height + count($lines), $this->table->getHeight()); + } + + public function addLineProvider() + { + return [ + 'empty' => [ + 'lines' => [], + ], + 'single' => [ + 'lines' => ['test'], + ], + 'multiple' => [ + 'lines' => ['test line 1', 'test line 2'], + ], + ]; + } + + public function testGetMaxHeight() + { + $this->assertEquals(self::MAX_HEIGHT - self::HEADER_HEIGHT - self::FOOTER_HEIGHT, $this->table->getMaxHeight()); + $this->table->disableHeader(); + $this->table->disableFooter(); + $this->assertEquals(self::MAX_HEIGHT, $this->table->getMaxHeight()); + } + + /** + * @param $tableLines + * @param $addLine + * @param $isValid + * + * @dataProvider addFitsLineProvider + */ + public function testFitsLine($tableLines, $addLine, $isValid) + { + foreach ($tableLines as $line) { + $tableLine = new Line(['test-field']); + $tableLine['test-field'] = $line; + + $this->table->addLine($tableLine); + } + + $line = new Line(['test-field']); + $line['test-field'] = $addLine; + + $this->assertEquals($isValid, $this->table->fitsLine($line)); + } + + public function addFitsLineProvider() + { + $singleLine = ['test add 1']; + $multiLine = ['test add 1', 'test add 2']; + $tooManyLine = ['test add 1', 'test add 2', 'test add 3', 'test add 4', 'test add 5']; + + $emptyTable = []; + $nonEmptyTable = ['test table 1', 'test table 2']; + $fullTable = ['test table 1', 'test table 2', 'test table 3', 'test table 4']; + + return [ + 'empty add single' => [ + 'tableLines' => $emptyTable, + 'addLine' => $singleLine, + 'isValid' => true, + ], + 'empty add multiple' => [ + 'tableLines' => $emptyTable, + 'addLine' => $multiLine, + 'isValid' => true, + ], + 'empty add too many' => [ + 'tableLines' => $emptyTable, + 'addLine' => $tooManyLine, + 'isValid' => false, + ], + 'nonempty add single' => [ + 'tableLines' => $nonEmptyTable, + 'addLine' => $singleLine, + 'isValid' => true, + ], + 'nonempty add multiple' => [ + 'tableLines' => $nonEmptyTable, + 'addLine' => $multiLine, + 'isValid' => true, + ], + 'nonempty add too many' => [ + 'tableLines' => $nonEmptyTable, + 'addLine' => $tooManyLine, + 'isValid' => false, + ], + 'full add single' => [ + 'tableLines' => $fullTable, + 'addLine' => $singleLine, + 'isValid' => false, + ], + 'full add multiple' => [ + 'tableLines' => $fullTable, + 'addLine' => $multiLine, + 'isValid' => false, + ], + 'full add too many' => [ + 'tableLines' => $fullTable, + 'addLine' => $tooManyLine, + 'isValid' => false, + ], + ]; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/DocumentTableProviderTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/DocumentTableProviderTest.php new file mode 100644 index 000000000..317959c53 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/DocumentTableProviderTest.php @@ -0,0 +1,62 @@ +provider = new DocumentTableProvider(); + } + + public function testGetTablesSupported() + { + /** @var TableProviderInterface|\PHPUnit_Framework_MockObject_MockObject $workingProvider */ + $workingProvider = $this->createMock(TableProviderInterface::class); + $workingProvider->expects($this->once()) + ->method('supports') + ->willReturn(true) + ; + $workingProvider->expects($this->once()) + ->method('getTables') + ->willReturn(new Table(20, 5, 4)) + ; + + /** @var TableProviderInterface|\PHPUnit_Framework_MockObject_MockObject $notWorkingProvider */ + $notWorkingProvider = $this->createMock(TableProviderInterface::class); + $notWorkingProvider->expects($this->once()) + ->method('supports') + ->willReturn(false) + ; + + $this->provider->addProvider($notWorkingProvider); + $this->provider->addProvider($workingProvider); + + $tables = $this->provider->getTables(new \stdClass()); + + $this->assertNotEmpty($tables); + } + + public function testGetTablesNotSupported() + { + /** @var TableProviderInterface|\PHPUnit_Framework_MockObject_MockObject $notWorkingProvider */ + $notWorkingProvider = $this->createMock(TableProviderInterface::class); + $notWorkingProvider->expects($this->once()) + ->method('supports') + ->willReturn(false) + ; + + $this->provider->addProvider($notWorkingProvider); + + $tables = $this->provider->getTables(new \stdClass()); + + $this->assertEmpty($tables); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/ConfigValuesProviderTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/ConfigValuesProviderTest.php new file mode 100644 index 000000000..393a930e2 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/ConfigValuesProviderTest.php @@ -0,0 +1,75 @@ +createMock(ConfigManager::class); + $parameters = []; + + $provider = new ConfigValuesProvider($configManager, $parameters); + + $this->assertTrue($provider->supports('test value', [])); + } + + public function testGetParams() + { + $parameters = [ + 'parameter-1' => 'value 1', + 'parameter-2' => 'value 2', + ]; + + /** @var ConfigManager|\PHPUnit_Framework_MockObject_MockObject $configManager */ + $configManager = $this->createMock(ConfigManager::class); + $configManager->expects($this->exactly(count($parameters))) + ->method('get') + ->withConsecutive(...array_map(function ($x) { return [$x, false, false, null]; }, array_keys($parameters))) + ->willReturnOnConsecutiveCalls(...array_values($parameters)) + ; + + $providerParameters = array_combine(array_keys($parameters), array_keys($parameters)); + + $provider = new ConfigValuesProvider($configManager, $providerParameters); + + $result = $provider->getParams('test value', []); + + $this->assertCount(count($parameters), $result); + foreach ($parameters as $key => $value) { + $this->assertEquals($value, $result[$key]); + } + } + + public function testGetParamsWithScope() + { + $parameters = [ + 'parameter-1' => 'value 1', + 'parameter-2' => 'value 2', + ]; + + /** @var ConfigManager|\PHPUnit_Framework_MockObject_MockObject $configManager */ + $configManager = $this->createMock(ConfigManager::class); + $configManager->expects($this->exactly(count($parameters))) + ->method('get') + ->withConsecutive(...array_map(function ($x) { return [$x, false, false, 'scope']; }, array_keys($parameters))) + ->willReturnOnConsecutiveCalls(...array_values($parameters)) + ; + + $providerParameters = array_combine(array_keys($parameters), array_keys($parameters)); + + $provider = new ConfigValuesProvider($configManager, $providerParameters); + + $result = $provider->getParams('test value', [ConfigValuesProvider::SCOPE_IDENTIFIER_KEY => 'scope']); + + $this->assertCount(count($parameters), $result); + foreach ($parameters as $key => $value) { + $this->assertEquals($value, $result[$key]); + } + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/EntityProviderTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/EntityProviderTest.php new file mode 100644 index 000000000..051164b29 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/Render/EntityProviderTest.php @@ -0,0 +1,29 @@ +provider = new EntityProvider(); + } + + public function testSupports() + { + $this->assertTrue($this->provider->supports('test value', [])); + } + + public function testGetParams() + { + $result = $this->provider->getParams('test value', []); + + $this->assertTrue(is_array($result)); + $this->assertEquals('test value', $result['entity']); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/RenderParametersProviderTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/RenderParametersProviderTest.php new file mode 100644 index 000000000..c06933f10 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/RenderParametersProviderTest.php @@ -0,0 +1,74 @@ +createMock(RenderParameterProviderInterface::class); + $firstProvider->expects($this->once()) + ->method('supports') + ->willReturn($firstProviderSupported) + ; + if ($firstProviderSupported) { + $firstProvider->expects($this->once()) + ->method('getParams') + ->willReturn(['first' => 'first value']) + ; + } + + /** @var RenderParameterProviderInterface|\PHPUnit_Framework_MockObject_MockObject $secondProvider */ + $secondProvider = $this->createMock(RenderParameterProviderInterface::class); + $secondProvider->expects($this->once()) + ->method('supports') + ->willReturn($secondProviderSupported) + ; + if ($secondProviderSupported) { + $secondProvider->expects($this->once()) + ->method('getParams') + ->willReturn(['second' => 'second value']) + ; + } + + $provider = new RenderParametersProvider(); + $provider->addProvider($firstProvider); + $provider->addProvider($secondProvider); + + $entity = new \stdClass(); + $this->assertEquals($result, $provider->getParams($entity, [])); + } + + public function getParamsProvider() + { + return [ + 'none supported' => [ + 'firstProviderSupported' => false, + 'secondProviderSupported' => false, + 'result' => [], + ], + 'one supported' => [ + 'firstProviderSupported' => true, + 'secondProviderSupported' => false, + 'result' => ['first' => 'first value'], + ], + 'multiple supported' => [ + 'firstProviderSupported' => true, + 'secondProviderSupported' => true, + 'result' => ['first' => 'first value', 'second' => 'second value'], + ], + ]; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/TableSizeProviderTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/TableSizeProviderTest.php new file mode 100644 index 000000000..d41b0c9d3 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Provider/TableSizeProviderTest.php @@ -0,0 +1,186 @@ +salesChannel = $this->getEntity(SalesChannel::class, ['id' => 1, 'name' => 'Test Sales Channel', 'code' => 'test1']); + } + + /** + * @param $maxHeight + * @param $maxTextWidth + * @param $firstPageInfoHeight + * @param $lastPageInfoHeight + * @param $pageSize + * + * @dataProvider dataProvider + */ + public function testGetMaxHeight($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize) + { + $provider = $this->getProvider($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize); + + $result = $this->getPageSizeProviderResult($maxHeight, $pageSize); + + $this->assertEquals($result, $provider->getMaxHeight($this->salesChannel)); + } + + /** + * @param $maxHeight + * @param $maxTextWidth + * @param $firstPageInfoHeight + * @param $lastPageInfoHeight + * @param $pageSize + * + * @dataProvider dataProvider + */ + public function testGetMaxTextWidth($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize) + { + $provider = $this->getProvider($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize); + + $result = $this->getPageSizeProviderResult($maxTextWidth, $pageSize); + + $this->assertEquals($result, $provider->getMaxTextWidth($this->salesChannel)); + } + + /** + * @param $maxHeight + * @param $maxTextWidth + * @param $firstPageInfoHeight + * @param $lastPageInfoHeight + * @param $pageSize + * + * @dataProvider dataProvider + */ + public function testGetFirstPageInfoHeight($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize) + { + $provider = $this->getProvider($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize); + + $result = $this->getPageSizeProviderResult($firstPageInfoHeight, $pageSize); + + $this->assertEquals($result, $provider->getFirstPageInfoHeight($this->salesChannel)); + } + + /** + * @param $maxHeight + * @param $maxTextWidth + * @param $firstPageInfoHeight + * @param $lastPageInfoHeight + * @param $pageSize + * + * @dataProvider dataProvider + */ + public function testGetLastPageInfoHeight($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize) + { + $provider = $this->getProvider($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize); + + $result = $this->getPageSizeProviderResult($lastPageInfoHeight, $pageSize); + + $this->assertEquals($result, $provider->getLastPageInfoHeight($this->salesChannel)); + } + + /** + * @return array + */ + public function dataProvider() + { + return [ + 'a4' => [ + 'maxHeight' => ['a4' => 10, 'letter' => 11], + 'maxTextWidth' => ['a4' => 12, 'letter' => 13], + 'firstPageInfoHeight' => ['a4' => 14, 'letter' => 15], + 'lastPageInfoHeight' => ['a4' => 16, 'letter' => 16], + 'pageSize' => Configuration::PAPER_SIZE_A4, + ], + 'letter' => [ + 'maxHeight' => ['a4' => 20, 'letter' => 21], + 'maxTextWidth' => ['a4' => 22, 'letter' => 23], + 'firstPageInfoHeight' => ['a4' => 14, 'letter' => 15], + 'lastPageInfoHeight' => ['a4' => 16, 'letter' => 17], + 'pageSize' => Configuration::PAPER_SIZE_LETTER, + ], + 'not compound' => [ + 'maxHeight' => 30, + 'maxTextWidth' => 32, + 'firstPageInfoHeight' => 34, + 'lastPageInfoHeight' => 36, + 'pageSize' => null, + ], + 'not set' => [ + 'maxHeight' => ['a4' => 20, 'letter' => 21], + 'maxTextWidth' => ['a4' => 22, 'letter' => 23], + 'firstPageInfoHeight' => ['a4' => 14, 'letter' => 15], + 'lastPageInfoHeight' => ['a4' => 16, 'letter' => 17], + 'pageSize' => 'not existing', + ], + ]; + } + + /** + * @param $maxHeight + * @param $maxTextWidth + * @param $firstPageInfoHeight + * @param $lastPageInfoHeight + * @param $pageSize + * @return TableSizeProvider + */ + protected function getProvider($maxHeight, $maxTextWidth, $firstPageInfoHeight, $lastPageInfoHeight, $pageSize) + { + /** @var SalesChannel $salesChannel */ + $salesChannel = $this->getEntity(SalesChannel::class, ['id' => 1, 'name' => 'Test Sales Channel', 'code' => 'test1']); + + /** @var ConfigManager|\PHPUnit_Framework_MockObject_MockObject $configManager */ + $configManager = $this->createMock(ConfigManager::class); + if ($pageSize !== null) { + $configManager->expects($this->once()) + ->method('get') + ->with('marello_pdf.paper_size', false, false, $salesChannel) + ->willReturn($pageSize) + ; + } + + return new TableSizeProvider( + $configManager, + $maxHeight, + $maxTextWidth, + $firstPageInfoHeight, + $lastPageInfoHeight + ); + } + + /** + * @param $values + * @param $pageSize + * @return mixed|null + */ + protected function getPageSizeProviderResult($values, $pageSize) + { + $result = null; + if (!is_array($values)) { + $result = $values; + } elseif (isset($values[$pageSize])) { + $result = $values[$pageSize]; + } else { + $this->expectException(PaperSizeNotSetException::class); + } + + return $result; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/HtmlRendererTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/HtmlRendererTest.php new file mode 100644 index 000000000..b01b3ea43 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/HtmlRendererTest.php @@ -0,0 +1,105 @@ +createMock(PdfWriterFactory::class); + $factory->expects($this->once()) + ->method('create') + ->willReturn(new Mpdf()) + ; + + $this->renderer = new HtmlRenderer($factory); + } + + public function tearDown() + { + $files = ['/tmp/pdf_single.pdf', '/tmp/pdf_multiple.pdf']; + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + /** + * @param $input + * @param $expected + * + * @dataProvider renderProvider + */ + public function testRender($input, $expected) + { + $generatedPdf = $this->renderer->render($input); + $expectedMagick = new \Imagick(); + $expectedMagick->readImageBlob($expected); + $expectedMagick->resetIterator(); + $expectedMagick = $expectedMagick->appendImages(true); + + $generatedImagick = new \Imagick(); + $generatedImagick->readImageBlob($generatedPdf); + $generatedImagick->resetIterator(); + $generatedImagick = $generatedImagick->appendImages(true); + + $diff = $expectedMagick->compareImages($generatedImagick, 1); + + $this->assertSame(0.0, $diff[1]); + } + + /** + * @param $input + * @param $expected + * + * @dataProvider renderProvider + */ + public function testRenderToFile($input, $expected, $filename) + { + $generatedPdf = $this->renderer->renderToFile($input, $filename); + + $expectedMagick = new \Imagick(); + $expectedMagick->readImageBlob($expected); + $expectedMagick->resetIterator(); + $expectedMagick = $expectedMagick->appendImages(true); + + $generatedImagick = new \Imagick(); + $generatedImagick->readImageFile(fopen($filename, 'r')); + $generatedImagick->resetIterator(); + $generatedImagick = $generatedImagick->appendImages(true); + + $diff = $expectedMagick->compareImages($generatedImagick, 1); + + $this->assertSame(0.0, $diff[1]); + } + + /** + * @return array + */ + public function renderProvider() + { + return [ + 'single page' => [ + 'input' => 'PDF test

Page 1

Page 1

', + 'expected' => base64_decode(file_get_contents(__DIR__.'/data/single.txt')), + 'filename' => '/tmp/pdf_single.pdf', + ], + 'multiple pages' => [ + 'input' => 'PDF test

Page 1

Page 1

Page 2

Page 2

', + 'expected' => base64_decode(file_get_contents(__DIR__.'/data/multiple.txt')), + 'filename' => '/tmp/pdf_multiple.pdf', + ], + ]; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/multiple.txt b/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/multiple.txt new file mode 100644 index 000000000..9d62e8b44 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/multiple.txt @@ -0,0 +1 @@ +JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9UeXBlIC9QYWdlCi9QYXJlbnQgMSAwIFIKL01lZGlhQm94IFswIDAgNTk1LjI4MCA4NDEuODkwXQovVHJpbUJveCBbMC4wMDAgMC4wMDAgNTk1LjI4MCA4NDEuODkwXQovUmVzb3VyY2VzIDIgMCBSCi9Hcm91cCA8PCAvVHlwZSAvR3JvdXAgL1MgL1RyYW5zcGFyZW5jeSAvQ1MgL0RldmljZVJHQiA+PiAKL0NvbnRlbnRzIDQgMCBSPj4KZW5kb2JqCjQgMCBvYmoKPDwvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE0MT4+CnN0cmVhbQp4nI2OsQoCQQxEU+crptQml4S9W2tBD6wUtxML4XTBTi38ffc8PNRCrBKYmTcDx4pV6ibizvOEamkwE1VFOmGRePhz8fS35ardGvJtzBTflXd7KLpRe3Ic7m+cCwZCBvoIiie41K6IMYg2M6QOmNCaDpTpSCCbIp1LFJtP8NfAl/ajIJi4+z8FDwKxOJwKZW5kc3RyZWFtCmVuZG9iago1IDAgb2JqCjw8L1R5cGUgL1BhZ2UKL1BhcmVudCAxIDAgUgovTWVkaWFCb3ggWzAgMCA1OTUuMjgwIDg0MS44OTBdCi9UcmltQm94IFswLjAwMCAwLjAwMCA1OTUuMjgwIDg0MS44OTBdCi9SZXNvdXJjZXMgMiAwIFIKL0dyb3VwIDw8IC9UeXBlIC9Hcm91cCAvUyAvVHJhbnNwYXJlbmN5IC9DUyAvRGV2aWNlUkdCID4+IAovQ29udGVudHMgNiAwIFI+PgplbmRvYmoKNiAwIG9iago8PC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTQzPj4Kc3RyZWFtCnicjY6xCgIxEES33q+YUpu9TXATrxX0wEoxnVgIpwE7tfD3zXkY0EKsdmFm3gw81qxiIeLBi4Rm5eCcqCrSGcvE45+LZ7gdN93OId9rpvhuvD9A0VftxfFwQaxyrhgJGRgiKJ6ZF/OKGFsJc0PqgQlt6EiZTgTyU6RLiWL7Cf4a+NZ+FJhKa38VPAEQDji9CmVuZHN0cmVhbQplbmRvYmoKMSAwIG9iago8PC9UeXBlIC9QYWdlcwovS2lkcyBbMyAwIFIgNSAwIFIgXQovQ291bnQgMgovTWVkaWFCb3ggWzAgMCA1OTUuMjgwIDg0MS44OTBdCj4+CmVuZG9iago3IDAgb2JqCjw8L1R5cGUgL0V4dEdTdGF0ZQovQk0gL05vcm1hbAovY2EgMQovQ0EgMQo+PgplbmRvYmoKOCAwIG9iago8PC9UeXBlIC9Gb250Ci9TdWJ0eXBlIC9UeXBlMAovQmFzZUZvbnQgL01QREZBQStEZWphVnVTZXJpZkNvbmRlbnNlZAovRW5jb2RpbmcgL0lkZW50aXR5LUgKL0Rlc2NlbmRhbnRGb250cyBbOSAwIFJdCi9Ub1VuaWNvZGUgMTAgMCBSCj4+CmVuZG9iago5IDAgb2JqCjw8L1R5cGUgL0ZvbnQKL1N1YnR5cGUgL0NJREZvbnRUeXBlMgovQmFzZUZvbnQgL01QREZBQStEZWphVnVTZXJpZkNvbmRlbnNlZAovQ0lEU3lzdGVtSW5mbyAxMSAwIFIKL0ZvbnREZXNjcmlwdG9yIDEyIDAgUgovRFcgNTQwCi9XIFsgMzIgWyAyODYgMzYxIDQxNCA3NTQgNTcyIDg1NSA4MDEgMjQ3IDM1MSAzNTEgNDUwIDc1NCAyODYgMzA0IDI4NiAzMDMgXQogNDggNTcgNTcyIDU4IDU5IDMwMyA2MCA2MiA3NTQgNjMgWyA0ODIgOTAwIDY1MCA2NjEgNjg4IDcyMSA2NTcgNjI0IDcxOSA3ODUgMzU1IDM2MCA2NzIgNTk4IDkyMSA3ODcgNzM4IDYwNSA3MzggNjc3IDYxNiA2MDAgNzU4IDY1MCA5MjUgNjQxIDU5NCA2MjUgMzUxIDMwMyAzNTEgNzU0IDQ1MCA0NTAgNTM2IDU3NiA1MDQgNTc2IDUzMiAzMzMgNTc2IDU4MCAyODggMjc5IDU0NSAyODggODUzIDU4MCA1NDIgNTc2IDU3NiA0MzAgNDYxIDM2MSA1ODAgNTA4IDc3MCA1MDcgNTA4IDQ3NCA1NzIgMzAzIDU3MiA3NTQgXQogXQovQ0lEVG9HSURNYXAgMTMgMCBSCj4+CmVuZG9iagoxMCAwIG9iago8PC9MZW5ndGggMzQ2Pj4Kc3RyZWFtCi9DSURJbml0IC9Qcm9jU2V0IGZpbmRyZXNvdXJjZSBiZWdpbgoxMiBkaWN0IGJlZ2luCmJlZ2luY21hcAovQ0lEU3lzdGVtSW5mbwo8PC9SZWdpc3RyeSAoQWRvYmUpCi9PcmRlcmluZyAoVUNTKQovU3VwcGxlbWVudCAwCj4+IGRlZgovQ01hcE5hbWUgL0Fkb2JlLUlkZW50aXR5LVVDUyBkZWYKL0NNYXBUeXBlIDIgZGVmCjEgYmVnaW5jb2Rlc3BhY2VyYW5nZQo8MDAwMD4gPEZGRkY+CmVuZGNvZGVzcGFjZXJhbmdlCjEgYmVnaW5iZnJhbmdlCjwwMDAwPiA8RkZGRj4gPDAwMDA+CmVuZGJmcmFuZ2UKZW5kY21hcApDTWFwTmFtZSBjdXJyZW50ZGljdCAvQ01hcCBkZWZpbmVyZXNvdXJjZSBwb3AKZW5kCmVuZAoKZW5kc3RyZWFtCmVuZG9iagoxMSAwIG9iago8PC9SZWdpc3RyeSAoQWRvYmUpCi9PcmRlcmluZyAoVUNTKQovU3VwcGxlbWVudCAwCj4+CmVuZG9iagoxMiAwIG9iago8PC9UeXBlIC9Gb250RGVzY3JpcHRvcgovRm9udE5hbWUgL01QREZBQStEZWphVnVTZXJpZkNvbmRlbnNlZAogL0NhcEhlaWdodCA3MjkKIC9YSGVpZ2h0IDUxOQogL0ZvbnRCQm94IFstNjkzIC0zNDcgMTUxMiAxMTA5XQogL0ZsYWdzIDQKIC9Bc2NlbnQgOTI4CiAvRGVzY2VudCAtMjM2CiAvTGVhZGluZyAwCiAvSXRhbGljQW5nbGUgMAogL1N0ZW1WIDg3CiAvTWlzc2luZ1dpZHRoIDU0MAogL1N0eWxlIDw8IC9QYW5vc2UgPCAwIDAgMiA2IDYgNiA1IDYgNSAyIDIgND4gPj4KL0ZvbnRGaWxlMiAxNCAwIFIKPj4KZW5kb2JqCjEzIDAgb2JqCjw8L0xlbmd0aCAzMDQKL0ZpbHRlciAvRmxhdGVEZWNvZGUKPj4Kc3RyZWFtCnic7c/nVggAAIDR75zsUWZkJXtkVCqEbC2UyIjo/V+ih+ifc+8b3Nqlgfa0t33t70AHO9ThjnS0wYY61vFOdLJTnW64M51tpHOd70IXu9RolxvrSle71vVudLNb3e5OdxvvXvd70MMmmmyqR00302yPe9LT5nrW814038te9bo3ve1d7/vQQosttdxKH/vU51Zb60vrfe1bG33vRz/71Wa/+9NWf/vX9m7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vbnQKL1N1YnR5cGUgL0NJREZvbnRUeXBlMgovQmFzZUZvbnQgL01QREZBQStEZWphVnVTZXJpZkNvbmRlbnNlZC1Cb2xkCi9DSURTeXN0ZW1JbmZvIDE4IDAgUgovRm9udERlc2NyaXB0b3IgMTkgMCBSCi9EVyA1NDAKL1cgWyAzMiBbIDMxMyAzOTUgNDY5IDc1NCA2MjYgODU1IDgxMyAyNzUgNDI2IDQyNiA0NzAgNzU0IDMxMyAzNzQgMzEzIDMyOSBdCiA0OCA1NyA2MjYgNTggNTkgMzMyIDYwIDYyIDc1NCA2MyBbIDUyNyA5MDAgNjk4IDc2MCA3MTYgNzgwIDY4NiA2MzkgNzY5IDg1MCA0MjEgNDI2IDc4MiA2MzMgOTk2IDgyMiA3ODQgNjc3IDc4NCA3NDggNjUwIDY2OSA3ODUgNjk4IDEwMTEgNjk4IDY0MiA2NTcgNDI2IDMyOSA0MjYgNzU0IDQ1MCA0NTAgNTgzIDYyOSA1NDggNjI5IDU3MiAzODcgNjI5IDY1NCAzNDIgMzI1IDYyNCAzNDIgOTUyIDY1NCA2MDAgNjI5IDYyOSA0NzQgNTA2IDQxNiA2NTQgNTIzIDc3NCA1MzYgNTIzIDUxMSA1NzkgMzI3IDU3OSA3NTQgXQogXQovQ0lEVG9HSURNYXAgMjAgMCBSCj4+CmVuZG9iagoxNyAwIG9iago8PC9MZW5ndGggMzQ2Pj4Kc3RyZWFtCi9DSURJbml0IC9Qcm9jU2V0IGZpbmRyZXNvdXJjZSBiZWdpbgoxMiBkaWN0IGJlZ2luCmJlZ2luY21hcAovQ0lEU3lzdGVtSW5mbwo8PC9SZWdpc3RyeSAoQWRvYmUpCi9PcmRlcmluZyAoVUNTKQovU3VwcGxlbWVudCAwCj4+IGRlZgovQ01hcE5hbWUgL0Fkb2JlLUlkZW50aXR5LVVDUyBkZWYKL0NNYXBUeXBlIDIgZGVmCjEgYmVnaW5jb2Rlc3BhY2VyYW5nZQo8MDAwMD4gPEZGRkY+CmVuZGNvZGVzcGFjZXJhbmdlCjEgYmVnaW5iZnJhbmdlCjwwMDAwPiA8RkZGRj4gPDAwMDA+CmVuZGJmcmFuZ2UKZW5kY21hcApDTWFwTmFtZSBjdXJyZW50ZGljdCAvQ01hcCBkZWZpbmVyZXNvdXJjZSBwb3AKZW5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hZ2VMYXlvdXQgL09uZUNvbHVtbgo+PgplbmRvYmoKeHJlZgowIDI0CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDg1NSAwMDAwMCBuIAowMDAwMDI3NTM3IDAwMDAwIG4gCjAwMDAwMDAwMTUgMDAwMDAgbiAKMDAwMDAwMDIyMyAwMDAwMCBuIAowMDAwMDAwNDM0IDAwMDAwIG4gCjAwMDAwMDA2NDIgMDAwMDAgbiAKMDAwMDAwMDk1MCAwMDAwMCBuIAowMDAwMDAxMDExIDAwMDAwIG4gCjAwMDAwMDExNjMgMDAwMDAgbiAKMDAwMDAwMTcwMyAwMDAwMCBuIAowMDAwMDAyMDk5IDAwMDAwIG4gCjAwMDAwMDIxNjggMDAwMDAgbiAKMDAwMDAwMjQ3NiAwMDAwMCBuIAowMDAwMDAyODUyIDAwMDAwIG4gCjAwMDAwMTQyMjEgMDAwMDAgbiAKMDAwMDAxNDM4MCAwMDAwMCBuIAowMDAwMDE0OTI3IDAwMDAwIG4gCjAwMDAwMTUzMjMgMDAwMDAgbiAKMDAwMDAxNTM5MiAwMDAwMCBuIAowMDAwMDE1NzExIDAwMDAwIG4gCjAwMDAwMTYwODcgMDAwMDAgbiAKMDAwMDAyNzY2NCAwMDAwMCBuIAowMDAwMDI3ODIwIDAwMDAwIG4gCnRyYWlsZXIKPDwKL1NpemUgMjQKL1Jvb3QgMjMgMCBSCi9JbmZvIDIyIDAgUgovSUQgWzw5NWYzMzNhYmJmYWQ5Y2I4NGJmZjQ4MjI3ZGQwMzQ2MD4gPDk1ZjMzM2FiYmZhZDljYjg0YmZmNDgyMjdkZDAzNDYwPl0KPj4Kc3RhcnR4cmVmCjI3OTMwCiUlRU9G \ No newline at end of file diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/single.txt b/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/single.txt new file mode 100644 index 000000000..88b6839d4 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Renderer/data/single.txt @@ -0,0 +1 @@ +JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9UeXBlIC9QYWdlCi9QYXJlbnQgMSAwIFIKL01lZGlhQm94IFswIDAgNTk1LjI4MCA4NDEuODkwXQovVHJpbUJveCBbMC4wMDAgMC4wMDAgNTk1LjI4MCA4NDEuODkwXQovUmVzb3VyY2VzIDIgMCBSCi9Hcm91cCA8PCAvVHlwZSAvR3JvdXAgL1MgL1RyYW5zcGFyZW5jeSAvQ1MgL0RldmljZVJHQiA+PiAKL0NvbnRlbnRzIDQgMCBSPj4KZW5kb2JqCjQgMCBvYmoKPDwvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE0MT4+CnN0cmVhbQp4nI2OsQoCQQxEU+crptQml4S9W2tBD6wUtxML4XTBTi38ffc8PNRCrBKYmTcDx4pV6ibizvOEamkwE1VFOmGRePhz8fS35ardGvJtzBTflXd7KLpRe3Ic7m+cCwZCBvoIiie41K6IMYg2M6QOmNCaDpTpSCCbIp1LFJtP8NfAl/ajIJi4+z8FDwKxOJwKZW5kc3RyZWFtCmVuZG9iagoxIDAgb2JqCjw8L1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUiBdCi9Db3VudCAxCi9NZWRpYUJveCBbMCAwIDU5NS4yODAgODQxLjg5MF0KPj4KZW5kb2JqCjUgMCBvYmoKPDwvVHlwZSAvRXh0R1N0YXRlCi9CTSAvTm9ybWFsCi9jYSAxCi9DQSAxCj4+CmVuZG9iago2IDAgb2JqCjw8L1R5cGUgL0ZvbnQKL1N1YnR5cGUgL1R5cGUwCi9CYXNlRm9udCAvTVBERkFBK0RlamFWdVNlcmlmQ29uZGVuc2VkCi9FbmNvZGluZyAvSWRlbnRpdHktSAovRGVzY2VuZGFudEZvbnRzIFs3IDAgUl0KL1RvVW5pY29kZSA4IDAgUgo+PgplbmRvYmoKNyAwIG9iago8PC9UeXBlIC9Gb250Ci9TdWJ0eXBlIC9DSURGb250VHlwZTIKL0Jhc2VGb250IC9NUERGQUErRGVqYVZ1U2VyaWZDb25kZW5zZWQKL0NJRFN5c3RlbUluZm8gOSAwIFIKL0ZvbnREZXNjcmlwdG9yIDEwIDAgUgovRFcgNTQwCi9XIFsgMzIgWyAyODYgMzYxIDQxNCA3NTQgNTcyIDg1NSA4MDEgMjQ3IDM1MSAzNTEgNDUwIDc1NCAyODYgMzA0IDI4NiAzMDMgXQogNDggNTcgNTcyIDU4IDU5IDMwMyA2MCA2MiA3NTQgNjMgWyA0ODIgOTAwIDY1MCA2NjEgNjg4IDcyMSA2NTcgNjI0IDcxOSA3ODUgMzU1IDM2MCA2NzIgNTk4IDkyMSA3ODcgNzM4IDYwNSA3MzggNjc3IDYxNiA2MDAgNzU4IDY1MCA5MjUgNjQxIDU5NCA2MjUgMzUxIDMwMyAzNTEgNzU0IDQ1MCA0NTAgNTM2IDU3NiA1MDQgNTc2IDUzMiAzMzMgNTc2IDU4MCAyODggMjc5IDU0NSAyODggODUzIDU4MCA1NDIgNTc2IDU3NiA0MzAgNDYxIDM2MSA1ODAgNTA4IDc3MCA1MDcgNTA4IDQ3NCA1NzIgMzAzIDU3MiA3NTQgXQogXQovQ0lEVG9HSURNYXAgMTEgMCBSCj4+CmVuZG9iago4IDAgb2JqCjw8L0xlbmd0aCAzNDY+PgpzdHJlYW0KL0NJREluaXQgL1Byb2NTZXQgZmluZHJlc291cmNlIGJlZ2luCjEyIGRpY3QgYmVnaW4KYmVnaW5jbWFwCi9DSURTeXN0ZW1JbmZvCjw8L1JlZ2lzdHJ5IChBZG9iZSkKL09yZGVyaW5nIChVQ1MpCi9TdXBwbGVtZW50IDAKPj4gZGVmCi9DTWFwTmFtZSAvQWRvYmUtSWRlbnRpdHktVUNTIGRlZgovQ01hcFR5cGUgMiBkZWYKMSBiZWdpbmNvZGVzcGFjZXJhbmdlCjwwMDAwPiA8RkZGRj4KZW5kY29kZXNwYWNlcmFuZ2UKMSBiZWdpbmJmcmFuZ2UKPDAwMDA+IDxGRkZGPiA8MDAwMD4KZW5kYmZyYW5nZQplbmRjbWFwCkNNYXBOYW1lIGN1cnJlbnRkaWN0IC9DTWFwIGRlZmluZXJlc291cmNlIHBvcAplbmQKZW5kCgplbmRzdHJlYW0KZW5kb2JqCjkgMCBvYmoKPDwvUmVnaXN0cnkgKEFkb2JlKQovT3JkZXJpbmcgKFVDUykKL1N1cHBsZW1lbnQgMAo+PgplbmRvYmoKMTAgMCBvYmoKPDwvVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnROYW1lIC9NUERGQUErRGVqYVZ1U2VyaWZ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wIDYyOSA2MjkgNDc0IDUwNiA0MTYgNjU0IDUyMyA3NzQgNTM2IDUyMyA1MTEgNTc5IDMyNyA1NzkgNzU0IF0KIF0KL0NJRFRvR0lETWFwIDE4IDAgUgo+PgplbmRvYmoKMTUgMCBvYmoKPDwvTGVuZ3RoIDM0Nj4+CnN0cmVhbQovQ0lESW5pdCAvUHJvY1NldCBmaW5kcmVzb3VyY2UgYmVnaW4KMTIgZGljdCBiZWdpbgpiZWdpbmNtYXAKL0NJRFN5c3RlbUluZm8KPDwvUmVnaXN0cnkgKEFkb2JlKQovT3JkZXJpbmcgKFVDUykKL1N1cHBsZW1lbnQgMAo+PiBkZWYKL0NNYXBOYW1lIC9BZG9iZS1JZGVudGl0eS1VQ1MgZGVmCi9DTWFwVHlwZSAyIGRlZgoxIGJlZ2luY29kZXNwYWNlcmFuZ2UKPDAwMDA+IDxGRkZGPgplbmRjb2Rlc3BhY2VyYW5nZQoxIGJlZ2luYmZyYW5nZQo8MDAwMD4gPEZGRkY+IDwwMDAwPgplbmRiZnJhbmdlCmVuZGNtYXAKQ01hcE5hbWUgY3VycmVudGRpY3QgL0NNYXAgZGVmaW5lcmVzb3VyY2UgcG9wCmVuZAplbmQKCmVuZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKPDwvUmVnaXN0cnkgKEFkb2JlKQovT3JkZXJpbmcgKFVDUykKL1N1cHBsZW1lbnQgMAo+PgplbmRvYmoKMTcgMCBvYmoKPDwvVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnROYW1lIC9NUERGQUErRGVqYVZ1U2VyaWZDb25kZW5zZWQtQm9sZAogL0NhcEhlaWdodCA3MjkKIC9YSGVpZ2h0IDUxOQogL0ZvbnRCQm94IFstNzUyIC0zODkgMTYxNyAxMTQ1XQogL0ZsYWdzIDI2MjE0OAogL0FzY2VudCA5MzkKIC9EZXNjZW50IC0yMzYKIC9MZWFkaW5nIDAKIC9JdGFsaWNBbmdsZSAwCiAvU3RlbVYgMTY1CiAvTWlzc2luZ1dpZHRoIDU0MAogL1N0eWxlIDw8IC9QYW5vc2UgPCAwIDAgMiA2IDggNiA1IDYgNSAyIDIgND4gPj4KL0ZvbnRGaWxlMiAxOSAwIFIKPj4KZW5kb2JqCjE4IDAgb2JqCjw8L0xlbmd0aCA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4vz+Irw/xTRF9ehFvGOp9lHiFE0+PYMnzKPEIJhqA1fQmkjeRUkSRBEliGBHbRIJ7SSSIkBRRgJZA4Z7gNhH5E3wE65etxsP+//xD/68Z6PoLR/8LJFTidAplbmRzdHJlYW0KZW5kb2JqCjIgMCBvYmoKPDwvUHJvY1NldCBbL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSV0KL0ZvbnQgPDwKL0YxIDYgMCBSCi9GMiAxMyAwIFIKPj4KL0V4dEdTdGF0ZSA8PAovR1MxIDUgMCBSCj4+Cj4+CmVuZG9iagoyMCAwIG9iago8PAovUHJvZHVjZXIgKP7/AG0AUABEAEYAIAA3AC4AMQAuADkpCi9UaXRsZSAo/v8AUABEAEYAIAB0AGUAcwB0KQovQ3JlYXRpb25EYXRlICgyMDE5MDgyODE0MTcxMyswMCcwMCcpCi9Nb2REYXRlICgyMDE5MDgyODE0MTcxMyswMCcwMCcpCj4+CmVuZG9iagoyMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMSAwIFIKL09wZW5BY3Rpb24gWzMgMCBSIC9YWVogbnVsbCBudWxsIDFdCi9QYWdlTGF5b3V0IC9PbmVDb2x1bW4KPj4KZW5kb2JqCnhyZWYKMCAyMgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDA0MzQgMDAwMDAgbiAKMDAwMDAyNzEwNiAwMDAwMCBuIAowMDAwMDAwMDE1IDAwMDAwIG4gCjAwMDAwMDAyMjMgMDAwMDAgbiAKMDAwMDAwMDUyMyAwMDAwMCBuIAowMDAwMDAwNTg0IDAwMDAwIG4gCjAwMDAwMDA3MzUgMDAwMDAgbiAKMDAwMDAwMTI3NCAwMDAwMCBuIAowMDAwMDAxNjY5IDAwMDAwIG4gCjAwMDAwMDE3MzcgMDAwMDAgbiAKMDAwMDAwMjA0NSAwMDAwMCBuIAowMDAwMDAyNDIxIDAwMDAwIG4gCjAwMDAwMTM3OTAgMDAwMDAgbiAKMDAwMDAxMzk0OSAwMDAwMCBuIAowMDAwMDE0NDk2IDAwMDAwIG4gCjAwMDAwMTQ4OTIgMDAwMDAgbiAKMDAwMDAxNDk2MSAwMDAwMCBuIAowMDAwMDE1MjgwIDAwMDAwIG4gCjAwMDAwMTU2NTYgMDAwMDAgbiAKMDAwMDAyNzIzMyAwMDAwMCBuIAowMDAwMDI3Mzg5IDAwMDAwIG4gCnRyYWlsZXIKPDwKL1NpemUgMjIKL1Jvb3QgMjEgMCBSCi9JbmZvIDIwIDAgUgovSUQgWzwxMzY5NGYwNjYzMTgzNjkwMmZjNDM1ZTVlOThkODk4Zj4gPDEzNjk0ZjA2NjMxODM2OTAyZmM0MzVlNWU5OGQ4OThmPl0KPj4Kc3RhcnR4cmVmCjI3NDk5CiUlRU9G \ No newline at end of file diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Twig/Extension/DocumentTableExtensionTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Twig/Extension/DocumentTableExtensionTest.php new file mode 100644 index 000000000..66b558029 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Twig/Extension/DocumentTableExtensionTest.php @@ -0,0 +1,63 @@ +createMock(DocumentTableProvider::class); + + $this->extension = new DocumentTableExtension($tableProvider); + } + + public function testGetFilters() + { + $this->assertCount(0, $this->extension->getFilters()); + } + + public function testGetNodeVisitors() + { + $this->assertCount(0, $this->extension->getNodeVisitors()); + } + + public function testGetOperators() + { + $this->assertCount(0, $this->extension->getOperators()); + } + + public function testGetTests() + { + $this->assertCount(0, $this->extension->getTests()); + } + + public function testGetTokenParsers() + { + $this->assertCount(0, $this->extension->getTokenParsers()); + } + + public function testGetFunctions() + { + $functions = $this->extension->getFunctions(); + + $this->assertCount(1, $functions); + + /** @var TwigFunction $twigFunction */ + $twigFunction = reset($functions); + $this->assertEquals('get_document_tables', $twigFunction->getName()); + $this->assertTrue(is_callable($twigFunction->getCallable())); + } + + public function testGetName() + { + $this->assertEquals(DocumentTableExtension::NAME, $this->extension->getName()); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Action/SendEmailTemplateAttachmentActionTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Action/SendEmailTemplateAttachmentActionTest.php new file mode 100644 index 000000000..36f1c35ba --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Action/SendEmailTemplateAttachmentActionTest.php @@ -0,0 +1,137 @@ +createMock(Processor::class); + /** @var EntityNameResolver|\PHPUnit_Framework_MockObject_MockObject $entityNameResolver */ + $entityNameResolver = $this->createMock(EntityNameResolver::class); + /** @var EmailRenderer|\PHPUnit_Framework_MockObject_MockObject $renderer */ + $renderer = $this->createMock(EmailRenderer::class); + /** @var ManagerRegistry|\PHPUnit_Framework_MockObject_MockObject $managerRegistry */ + $managerRegistry = $this->createMock(ManagerRegistry::class); + /** @var ValidatorInterface|\PHPUnit_Framework_MockObject_MockObject $validator */ + $validator = $this->createMock(ValidatorInterface::class); + /** @var EmailOriginHelper|\PHPUnit_Framework_MockObject_MockObject $emailOriginHelper */ + $emailOriginHelper = $this->getMockBuilder(EmailOriginHelper::class)->disableOriginalConstructor()->getMock(); + + $this->action = new SendEmailTemplateAttachmentAction( + new ContextAccessor(), + $emailProcessor, + new EmailAddressHelper(), + $entityNameResolver, + $managerRegistry, + $validator, + $emailOriginHelper, + $renderer, + ); + } + + /** + * @dataProvider initializeProvider + */ + public function testInitialize($options, $exception = null, $exceptionMessage = null) + { + if ($exception !== null) { + $this->expectException($exception); + if ($exceptionMessage !== null) { + $this->expectExceptionMessage($exceptionMessage); + } + } + + $this->action->initialize($options); + } + + public function initializeProvider() + { + $baseOptions = [ + 'from' => 'test@example.com', + 'to' => 'test@example.com', + 'template' => 'template-name', + 'subject' => 'subject', + 'entity' => new \stdClass(), + ]; + + return [ + 'valid bcc simple' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_BCC => 'test@example.com', + ]), + ], + 'valid bcc array' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_BCC => [ + 'email' => 'test@example.com', + 'name' => 'test bcc' + ], + ]), + ], + 'invalid bcc' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_BCC => ['name' => 'test bcc'], + ]), + 'exception' => InvalidArgumentException::class, + 'exceptionMessage' => 'Email parameter is required', + ], + 'empty_bcc' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_BCC => null, + ]), + ], + 'attachments not array' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_ATTACHMENTS => 'attachments', + ]), + 'exception' => InvalidArgumentException::class, + 'exceptionMessage' => 'Attachments should be array', + ], + 'attachment options not array' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_ATTACHMENTS => ['attachments'], + ]), + 'exception' => InvalidArgumentException::class, + 'exceptionMessage' => 'Attachment options invalid', + ], + 'body or file not set' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_ATTACHMENTS => [ + [] + ], + ]), + 'exception' => InvalidArgumentException::class, + 'exceptionMessage' => 'Attachment option "body" or "file" should be set', + ], + 'body and file set' => [ + 'options' => array_merge($baseOptions, [ + SendEmailTemplateAttachmentAction::OPTION_ATTACHMENTS => [ + [ + SendEmailTemplateAttachmentAction::OPTION_ATTACHMENT_BODY => 'body', + SendEmailTemplateAttachmentAction::OPTION_ATTACHMENT_FILE => 'file', + ] + ], + ]), + 'exception' => InvalidArgumentException::class, + 'exceptionMessage' => 'Only one of options "body" and "file" should be set', + ], + ]; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Condition/IsSendEmailTransitionTest.php b/src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Condition/IsSendEmailTransitionTest.php new file mode 100644 index 000000000..2bbbd2c1a --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Tests/Unit/Workflow/Condition/IsSendEmailTransitionTest.php @@ -0,0 +1,94 @@ +configManager = $this->createMock(ConfigManager::class); + + $this->isSendTransition = new IsSendEmailTransition($this->configManager); + $this->isSendTransition->setContextAccessor(new ContextAccessor()); + } + + /** + * @dataProvider isConditionAllowedProvider + */ + public function testIsConditionAllowed($configTransition, $context, $options, $salesChannel, $allowed) + { + $this->configManager->expects($this->once()) + ->method('get') + ->with('marello_pdf.email_workflow_transition', false, false, $salesChannel) + ->willReturn($configTransition) + ; + + $this->isSendTransition->initialize($options); + + $this->assertEquals($allowed, $this->isSendTransition->isConditionAllowed($context)); + } + + public function isConditionAllowedProvider() + { + $salesChannel = $this->getEntity(SalesChannel::class, [ + 'id' => 1, + 'name' => 'test channel', + 'code' => 'test', + ]); + + $options = [ + IsSendEmailTransition::OPTION_CURRENT_TRANSITION => 'provided', + IsSendEmailTransition::OPTION_CONFIG_SCOPE => new PropertyPath('scope'), + ]; + + $context = [ + 'scope' => $salesChannel, + ]; + + return [ + 'not_set' => [ + 'configTransition' => null, + 'context' => $context, + 'options' => $options, + 'salesChannel' => $salesChannel, + 'allowed' => false, + ], + 'allowed' => [ + 'configTransition' => 'provided', + 'context' => $context, + 'options' => $options, + 'salesChannel' => $salesChannel, + 'allowed' => true, + ], + 'not_allowed' => [ + 'configTransition' => 'config', + 'context' => $context, + 'options' => $options, + 'salesChannel' => $salesChannel, + 'allowed' => false, + ], + ]; + } + + public function testGetName() + { + $this->assertEquals(IsSendEmailTransition::NAME, $this->isSendTransition->getName()); + } +} diff --git a/src/Marello/Bundle/PdfBundle/Twig/Extension/DocumentTableExtension.php b/src/Marello/Bundle/PdfBundle/Twig/Extension/DocumentTableExtension.php new file mode 100644 index 000000000..433344d6d --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Twig/Extension/DocumentTableExtension.php @@ -0,0 +1,31 @@ +tableProvider = $tableProvider; + } + + public function getFunctions() + { + return [ + new TwigFunction('get_document_tables', [$this->tableProvider, 'getTables']), + ]; + } + + public function getName() + { + return self::NAME; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Workflow/Action/SendEmailTemplateAttachmentAction.php b/src/Marello/Bundle/PdfBundle/Workflow/Action/SendEmailTemplateAttachmentAction.php new file mode 100644 index 000000000..00dcf56f6 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Workflow/Action/SendEmailTemplateAttachmentAction.php @@ -0,0 +1,375 @@ +registry = $registry; + $this->validator = $validator; + $this->renderer = $renderer; + $this->emailOriginHelper = $emailOriginHelper; + } + + /** + * @param array $options + * @return SendEmailTemplate + */ + public function initialize(array $options): self + { + if (isset($options[self::OPTION_BCC])) { + $this->assertEmailAddressOption($options[self::OPTION_BCC]); + } + if (empty($options['from'])) { + throw new InvalidParameterException('From parameter is required'); + } + + $this->assertEmailAddressOption($options['from']); + + if (empty($options['to'])) { + throw new InvalidParameterException('Need to specify "to" parameters'); + } + + $options = $this->normalizeToOption($options); + + if (empty($options['template'])) { + throw new InvalidParameterException('Template parameter is required'); + } + + if (empty($options['entity'])) { + throw new InvalidParameterException('Entity parameter is required'); + } + + if (isset($options[self::OPTION_ATTACHMENTS])) { + $attachments = $options[self::OPTION_ATTACHMENTS]; + + if (!is_array($attachments)) { + throw new InvalidArgumentException('Attachments should be array'); + } + foreach ($attachments as $attachment) { + if (!is_array($attachment)) { + throw new InvalidArgumentException('Attachment options invalid'); + } + + if (!isset($attachment[self::OPTION_ATTACHMENT_BODY]) + && !isset($attachment[self::OPTION_ATTACHMENT_FILE]) + ) { + throw new InvalidArgumentException(sprintf( + 'Attachment option "%s" or "%s" should be set', + self::OPTION_ATTACHMENT_BODY, + self::OPTION_ATTACHMENT_FILE + )); + } + if (isset($attachment[self::OPTION_ATTACHMENT_BODY]) + && isset($attachment[self::OPTION_ATTACHMENT_FILE]) + ) { + throw new InvalidArgumentException(sprintf( + 'Only one of options "%s" and "%s" should be set', + self::OPTION_ATTACHMENT_BODY, + self::OPTION_ATTACHMENT_FILE + )); + } + } + } + + $this->options = $options; + $this->emailConstraint = new EmailConstraints(['message' => 'Invalid email address']); + + return $this; + } + + /** + * @param mixed $context + * @throws EntityNotFoundException + * @throws \Twig\Error\Error + * @throws \Twig_Error + */ + public function executeAction($context): void + { + $emailModel = new Email(); + + $from = $this->getEmailAddress($context, $this->options['from']); + $this->validateAddress($from); + $emailModel->setFrom($from); + $to = []; + + foreach ($this->options['to'] as $email) { + if ($email) { + $address = $this->getEmailAddress($context, $email); + $this->validateAddress($address); + $to[] = $address; + } + } + $emailModel->setTo($to); + $entity = $this->contextAccessor->getValue($context, $this->options['entity']); + $template = $this->contextAccessor->getValue($context, $this->options['template']); + + $emailTemplate = $this + ->registry + ->getManagerForClass(\get_class($entity)) + ->getRepository(EmailTemplate::class) + ->findByName($template) + ; + if (!$emailTemplate) { + $errorMessage = sprintf('Template "%s" not found.', $template); + $this->logger->error('Workflow send email action.' . $errorMessage); + throw new EntityNotFoundException($errorMessage); + } + $templateData = $this->renderer->compileMessage($emailTemplate, ['entity' => $entity]); + + list ($subjectRendered, $templateRendered) = $templateData; + + $emailModel->setSubject($subjectRendered); + $emailModel->setBody($templateRendered); + $emailModel->setType($emailTemplate->getType()); + $emailModel->setBcc($this->getBcc($context)); + + $this->addAttachments($emailModel, $context); + + $emailUser = null; + try { + $emailOrigin = $this->emailOriginHelper->getEmailOrigin( + $emailModel->getFrom(), + $emailModel->getOrganization() + ); + + $emailUser = $this->emailProcessor->process($emailModel, $emailOrigin); + } catch (\Swift_SwiftException $exception) { + $this->logger->error('Workflow send email template action.', ['exception' => $exception]); + } + + if (array_key_exists('attribute', $this->options) && $emailUser instanceof EmailUser) { + $this->contextAccessor->setValue($context, $this->options['attribute'], $emailUser->getEmail()); + } + } + + /** + * @param $context + * @return array|string + */ + protected function getBcc($context) + { + if (isset($this->options[self::OPTION_BCC])) { + $bcc = $this->getEmailAddress($context, $this->options[self::OPTION_BCC]); + $this->validateAddress($bcc); + + $bcc = [$bcc]; + } else { + $bcc = []; + } + + return array_filter($bcc); + } + + /** + * @param Email $emailModel + * @param $context + */ + protected function addAttachments(Email $emailModel, $context) + { + if (isset($this->options[self::OPTION_ATTACHMENTS])) { + $attachments = $this->options[self::OPTION_ATTACHMENTS]; + foreach ($attachments as $attachment) { + $emailModel->addAttachment($this->buildAttachment($attachment, $context)); + } + } + } + + /** + * @param $attachment + * @param $context + * @return EmailAttachment + */ + protected function buildAttachment($attachment, $context) + { + if (isset($attachment[self::OPTION_ATTACHMENT_FILE])) { + $emailAttachment = $this->buildFileAttachment($attachment, $context); + } else { + $emailAttachment = $this->buildStringAttachment($attachment, $context); + } + + return $emailAttachment; + } + + /** + * @param $attachment + * @param $context + * @return EmailAttachment + */ + protected function buildFileAttachment($attachment, $context) + { + $path = $this->contextAccessor->getValue($context, $attachment[self::OPTION_ATTACHMENT_FILE]); + $content = base64_encode(file_get_contents($path)); + if (isset($attachment[self::OPTION_ATTACHMENT_MIMETYPE])) { + $mimetype = $this->contextAccessor->getValue($context, $attachment[self::OPTION_ATTACHMENT_MIMETYPE]); + } else { + $extension = pathinfo($path, PATHINFO_EXTENSION); + $mimetype = $this->getMimeTypeGuesser()->guess($extension); + } + if (isset($attachment[self::OPTION_ATTACHMENT_FILENAME])) { + $filename = $this->contextAccessor->getValue($context, $attachment[self::OPTION_ATTACHMENT_FILENAME]); + } else { + $filename = pathinfo($path, PATHINFO_BASENAME); + } + + return $this->buildAttachmentFromString($content, $filename, $mimetype); + } + + /** + * @param $attachment + * @param $context + * @return EmailAttachment + */ + protected function buildStringAttachment($attachment, $context) + { + $content = $this->contextAccessor->getValue($context, $attachment[self::OPTION_ATTACHMENT_BODY]); + $filename = $this->contextAccessor->getValue($context, $attachment[self::OPTION_ATTACHMENT_FILENAME]); + $mimetype = $this->contextAccessor->getValue($context, $attachment[self::OPTION_ATTACHMENT_MIMETYPE]); + + return $this->buildAttachmentFromString($content, $filename, $mimetype); + } + + /** + * @param $content + * @param $filename + * @param $mimetype + * @return EmailAttachment + */ + protected function buildAttachmentFromString($content, $filename, $mimetype) + { + $attachmentEntity = new AttachmentEntity(); + + $attachmentContent = new EmailAttachmentContent(); + $attachmentContent->setContent($content); + $attachmentContent->setContentTransferEncoding('base64'); + $attachmentContent->setEmailAttachment($attachmentEntity); + + $attachmentEntity->setContent($attachmentContent); + $attachmentEntity->setContentType($mimetype); + $attachmentEntity->setFileName($filename); + + $emailAttachment = new EmailAttachment(); + $emailAttachment->setType(EmailAttachment::TYPE_EMAIL_ATTACHMENT); + $emailAttachment->setEmailAttachment($attachmentEntity); + + return $emailAttachment; + } + + /** + * @return MimeTypeGuesser + */ + protected function getMimeTypeGuesser() + { + if ($this->mimeTypeGuesser === null) { + $this->mimeTypeGuesser = MimeTypeGuesser::getInstance(); + } + + return $this->mimeTypeGuesser; + } + + /** + * @param string $email + * @throws ValidatorException + */ + protected function validateAddress($email): void + { + $errorList = $this->validator->validate($email, $this->emailConstraint); + + if ($errorList && $errorList->count() > 0) { + throw new ValidatorException($errorList->get(0)->getMessage()); + } + } + + /** + * @param array $options + * @return array + */ + protected function normalizeToOption(array $options): array + { + if (empty($options['to'])) { + $options['to'] = []; + } + if (!is_array($options['to']) + || array_key_exists('name', $options['to']) + || array_key_exists('email', $options['to']) + ) { + $options['to'] = [$options['to']]; + } + + foreach ($options['to'] as $to) { + $this->assertEmailAddressOption($to); + } + + return $options; + } +} diff --git a/src/Marello/Bundle/PdfBundle/Workflow/Condition/IsSendEmailTransition.php b/src/Marello/Bundle/PdfBundle/Workflow/Condition/IsSendEmailTransition.php new file mode 100644 index 000000000..6bbcef491 --- /dev/null +++ b/src/Marello/Bundle/PdfBundle/Workflow/Condition/IsSendEmailTransition.php @@ -0,0 +1,70 @@ +configManager = $configManager; + } + + public function initialize(array $options) + { + $this->currentTransition = $options[self::OPTION_CURRENT_TRANSITION]; + $this->configScope = $options[self::OPTION_CONFIG_SCOPE]; + + return $this; + } + + public function isConditionAllowed($context) + { + $currentTransition = $this->contextAccessor->getValue($context, $this->currentTransition); + $configScope = $this->contextAccessor->getValue($context, $this->configScope); + + $configKey = sprintf('%s.%s', Configuration::CONFIG_NAME, Configuration::CONFIG_KEY_EMAIL_WORKFLOW_TRANSITION); + + $configuredTransitions = $this->configManager->get($configKey, false, false, $configScope); + if ($configuredTransitions === null) { + $configuredTransitions = []; + } + if (is_scalar($configuredTransitions)) { + $configuredTransitions = [$configuredTransitions]; + } + + return in_array($currentTransition, $configuredTransitions); + } + + public function getName() + { + return self::NAME; + } + + public function toArray() + { + return $this->convertToArray([$this->currentTransition]); + } + + public function compile($factoryAccessor) + { + return $this->convertToPhpCode([$this->currentTransition], $factoryAccessor); + } +} diff --git a/src/Marello/Bundle/PricingBundle/Controller/PricingController.php b/src/Marello/Bundle/PricingBundle/Controller/PricingController.php index 062061631..9a7db55d3 100644 --- a/src/Marello/Bundle/PricingBundle/Controller/PricingController.php +++ b/src/Marello/Bundle/PricingBundle/Controller/PricingController.php @@ -2,17 +2,20 @@ namespace Marello\Bundle\PricingBundle\Controller; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; +use Symfony\Component\Routing\Annotation\Route; -class PricingController extends Controller +class PricingController extends AbstractController { /** - * @Config\Route("/get-currency-by-channel", name="marello_pricing_currency_by_channel") - * @Config\Method({"GET"}) + * @Route( + * path="/get-currency-by-channel", + * methods={"GET"}, + * name="marello_pricing_currency_by_channel" + * ) * @AclAncestor("marello_sales_saleschannel_view") * * {@inheritdoc} diff --git a/src/Marello/Bundle/PricingBundle/EventListener/Datagrid/ChannelPricesDatagridListener.php b/src/Marello/Bundle/PricingBundle/EventListener/Datagrid/ChannelPricesDatagridListener.php new file mode 100644 index 000000000..d3445f71b --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/EventListener/Datagrid/ChannelPricesDatagridListener.php @@ -0,0 +1,190 @@ +relatedEntityClass = $relatedEntityClass; + + $this->expressionBuilder = new Expr(); + } + + /** + * @param BuildBefore $event + */ + public function onBuildBefore(BuildBefore $event) + { + $config = $event->getConfig(); + + $this->addSelect($config); + $this->addJoin($config); + $this->addColumn($config); + $this->addSorter($config); + $this->addFilter($config); + } + + /** + * @param DatagridConfiguration $configuration + * @return string + * @throws \InvalidArgumentException when a root entity not found in the grid + */ + protected function getAlias(DatagridConfiguration $configuration) + { + $rootAlias = $configuration->getOrmQuery()->getRootAlias(); + if (!$rootAlias) { + throw new \InvalidArgumentException( + sprintf( + 'A root entity is missing for grid "%s"', + $configuration->getName() + ) + ); + } + + return $rootAlias; + } + + /** + * @return string + */ + protected function getDataName() + { + return self::DATA_NAME; + } + + /** + * @return string + */ + protected function getJoinAlias() + { + return self::JOIN_ALIAS; + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSelect(DatagridConfiguration $config) + { + $ormQuery = $config->getOrmQuery(); + $ormQuery + ->addSelect( + sprintf( + 'GROUP_CONCAT( + DISTINCT CONCAT_WS(\'|\', %1$s.name, %2$s.value, %2$s.currency) SEPARATOR \';\' + ) as defaultChannelPrices', + self::CHANNEL_JOIN_ALIAS, + self::DEFAULT_JOIN_ALIAS + ) + ) + ->addSelect( + sprintf( + 'GROUP_CONCAT( + DISTINCT CONCAT_WS(\'|\', %1$s.name, %2$s.value, %2$s.currency) SEPARATOR \';\' + ) as specialChannelPrices', + self::CHANNEL_JOIN_ALIAS, + self::SPECIAL_JOIN_ALIAS + ) + ) + ->addSelect(sprintf('sum(%s.value) as defaultChannelPricesSum', self::DEFAULT_JOIN_ALIAS)) + ->addSelect(sprintf('sum(%s.value) as specialChannelPricesSum', self::SPECIAL_JOIN_ALIAS)); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addJoin(DatagridConfiguration $config) + { + $ormQuery = $config->getOrmQuery(); + $ormQuery + ->addLeftJoin(sprintf('%s.%s', $this->getAlias($config), self::DATA_NAME), $this->getJoinAlias()) + ->addLeftJoin(sprintf('%s.%s', $this->getJoinAlias(), self::CHANNEL_DATA_NAME), self::CHANNEL_JOIN_ALIAS) + ->addLeftJoin(sprintf('%s.%s', $this->getJoinAlias(), self::DEFAULT_DATA_NAME), self::DEFAULT_JOIN_ALIAS) + ->addLeftJoin(sprintf('%s.%s', $this->getJoinAlias(), self::SPECIAL_DATA_NAME), self::SPECIAL_JOIN_ALIAS); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addColumn(DatagridConfiguration $config) + { + $config->offsetSetByPath(sprintf('[columns][%s]', 'defaultChannelPrices'), [ + 'label' => 'marello.pricing.assembledchannelpricelist.default_price.plural_label', + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloPricingBundle:Datagrid/Property:defaultChannelPrices.html.twig', + 'renderable' => false, + 'align' => 'right' + ]); + $config->offsetSetByPath(sprintf('[columns][%s]', 'specialChannelPrices'), [ + 'label' => 'marello.pricing.assembledchannelpricelist.special_price.plural_label', + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloPricingBundle:Datagrid/Property:specialChannelPrices.html.twig', + 'renderable' => false, + 'align' => 'right' + ]); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSorter(DatagridConfiguration $config) + { + $config + ->offsetSetByPath( + sprintf('[sorters][columns][%s]', 'defaultChannelPrices'), + ['data_name' => 'defaultChannelPricesSum'] + ) + ->offsetSetByPath( + sprintf('[sorters][columns][%s]', 'specialChannelPrices'), + ['data_name' => 'specialChannelPricesSum'] + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addFilter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', 'defaultChannelPrices'), + [ + 'type' => 'number', + 'data_name' => self::DEFAULT_JOIN_ALIAS . '.value', + 'enabled' => false, + ] + ); + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', 'specialChannelPrices'), + [ + 'type' => 'number', + 'data_name' => self::SPECIAL_JOIN_ALIAS . '.value', + 'enabled' => false, + ] + ); + } +} diff --git a/src/Marello/Bundle/PricingBundle/EventListener/Datagrid/PricesDatagridListener.php b/src/Marello/Bundle/PricingBundle/EventListener/Datagrid/PricesDatagridListener.php new file mode 100644 index 000000000..0a0fa4b17 --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/EventListener/Datagrid/PricesDatagridListener.php @@ -0,0 +1,208 @@ +relatedEntityClass = $relatedEntityClass; + + $this->expressionBuilder = new Expr(); + } + + /** + * @param BuildBefore $event + */ + public function onBuildBefore(BuildBefore $event) + { + $config = $event->getConfig(); + + $this->addSelect($config); + $this->addJoin($config); + $this->addColumn($config); + $this->addSorter($config); + $this->addFilter($config); + } + + /** + * @param DatagridConfiguration $configuration + * @return string + * @throws \InvalidArgumentException when a root entity not found in the grid + */ + protected function getAlias(DatagridConfiguration $configuration) + { + $rootAlias = $configuration->getOrmQuery()->getRootAlias(); + if (!$rootAlias) { + throw new \InvalidArgumentException( + sprintf( + 'A root entity is missing for grid "%s"', + $configuration->getName() + ) + ); + } + + return $rootAlias; + } + + /** + * @return string + */ + protected function getDataName() + { + return self::DATA_NAME; + } + + /** + * @return string + */ + protected function getJoinAlias() + { + return self::JOIN_ALIAS; + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSelect(DatagridConfiguration $config) + { + $ormQuery = $config->getOrmQuery(); + $ormQuery + ->addSelect( + sprintf( + 'GROUP_CONCAT( + DISTINCT CONCAT_WS(\'|\', %1$s.value, %1$s.currency) SEPARATOR \';\' + ) as defaultPrices', + self::DEFAULT_JOIN_ALIAS + ) + ) + ->addSelect( + sprintf( + 'GROUP_CONCAT( + DISTINCT CONCAT_WS(\'|\', %1$s.value, %1$s.currency) SEPARATOR \';\' + ) as specialPrices', + self::SPECIAL_JOIN_ALIAS + ) + ) + ->addSelect( + sprintf( + 'GROUP_CONCAT( + DISTINCT CONCAT_WS(\'|\', %1$s.value, %1$s.currency) SEPARATOR \';\' + ) as msrpPrices', + self::MSRP_JOIN_ALIAS + ) + ) + ->addSelect(sprintf('SUM(%s.value) as defaultPricesSum', self::DEFAULT_JOIN_ALIAS)) + ->addSelect(sprintf('SUM(%s.value) as specialPricesSum', self::SPECIAL_JOIN_ALIAS)) + ->addSelect(sprintf('SUM(%s.value) as msrpPricesSum', self::MSRP_JOIN_ALIAS)); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addJoin(DatagridConfiguration $config) + { + $ormQuery = $config->getOrmQuery(); + $ormQuery + ->addLeftJoin(sprintf('%s.%s', $this->getAlias($config), self::DATA_NAME), $this->getJoinAlias()) + ->addLeftJoin(sprintf('%s.%s', $this->getJoinAlias(), self::DEFAULT_DATA_NAME), self::DEFAULT_JOIN_ALIAS) + ->addLeftJoin(sprintf('%s.%s', $this->getJoinAlias(), self::SPECIAL_DATA_NAME), self::SPECIAL_JOIN_ALIAS) + ->addLeftJoin(sprintf('%s.%s', $this->getJoinAlias(), self::MSRP_DATA_NAME), self::MSRP_JOIN_ALIAS); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addColumn(DatagridConfiguration $config) + { + $config->offsetSetByPath(sprintf('[columns][%s]', 'defaultPrices'), [ + 'label' => 'marello.pricing.assembledpricelist.default_price.plural_label', + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloPricingBundle:Datagrid/Property:defaultPrices.html.twig', + 'renderable' => false, + 'align' => 'right' + ]); + $config->offsetSetByPath(sprintf('[columns][%s]', 'specialPrices'), [ + 'label' => 'marello.pricing.assembledpricelist.special_price.plural_label', + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloPricingBundle:Datagrid/Property:specialPrices.html.twig', + 'renderable' => false, + 'align' => 'right' + ]); + $config->offsetSetByPath(sprintf('[columns][%s]', 'msrpPrices'), [ + 'label' => 'marello.pricing.assembledpricelist.msrp_price.plural_label', + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloPricingBundle:Datagrid/Property:msrpPrices.html.twig', + 'renderable' => false, + 'align' => 'right' + ]); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSorter(DatagridConfiguration $config) + { + $config + ->offsetSetByPath(sprintf('[sorters][columns][%s]', 'defaultPrices'), ['data_name' => 'defaultPricesSum']) + ->offsetSetByPath(sprintf('[sorters][columns][%s]', 'specialPrices'), ['data_name' => 'specialPricesSum']) + ->offsetSetByPath(sprintf('[sorters][columns][%s]', 'msrpPrices'), ['data_name' => 'msrpPricesSum']); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addFilter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', 'defaultPrices'), + [ + 'type' => 'number', + 'data_name' => self::DEFAULT_JOIN_ALIAS . '.value', + 'enabled' => false, + ] + ); + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', 'specialPrices'), + [ + 'type' => 'number', + 'data_name' => self::SPECIAL_JOIN_ALIAS . '.value', + 'enabled' => false, + ] + ); + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', 'msrpPrices'), + [ + 'type' => 'number', + 'data_name' => self::MSRP_JOIN_ALIAS . '.value', + 'enabled' => false, + ] + ); + } +} diff --git a/src/Marello/Bundle/PricingBundle/Form/EventListener/PricingSubscriber.php b/src/Marello/Bundle/PricingBundle/Form/EventListener/PricingSubscriber.php index dded8a447..33ee0d1d3 100644 --- a/src/Marello/Bundle/PricingBundle/Form/EventListener/PricingSubscriber.php +++ b/src/Marello/Bundle/PricingBundle/Form/EventListener/PricingSubscriber.php @@ -11,7 +11,7 @@ use Marello\Bundle\PricingBundle\Entity\ProductPrice; use Marello\Bundle\PricingBundle\Provider\CurrencyProvider; use Marello\Bundle\PricingBundle\Model\PricingAwareInterface; -use Marello\Bundle\SalesBundle\Model\SalesChannelAwareInterface; +use Marello\Bundle\SalesBundle\Model\SalesChannelsAwareInterface; use Marello\Bundle\PricingBundle\Entity\AssembledPriceList; use Marello\Bundle\PricingBundle\Entity\PriceType; use Marello\Bundle\PricingBundle\Form\Type\AssembledPriceListCollectionType; @@ -67,7 +67,7 @@ public function preSetData(FormEvent $event) $entity = $event->getData(); $form = $event->getForm(); - if ($entity instanceof PricingAwareInterface && $entity instanceof SalesChannelAwareInterface) { + if ($entity instanceof PricingAwareInterface && $entity instanceof SalesChannelsAwareInterface) { $currencies = $this->provider->getCurrencies($entity->getChannels()); if ($entity->hasPrices()) { $existingCurrencies = []; diff --git a/src/Marello/Bundle/PricingBundle/Form/Type/AssembledChannelPriceListType.php b/src/Marello/Bundle/PricingBundle/Form/Type/AssembledChannelPriceListType.php index 35437b5be..2f282883c 100644 --- a/src/Marello/Bundle/PricingBundle/Form/Type/AssembledChannelPriceListType.php +++ b/src/Marello/Bundle/PricingBundle/Form/Type/AssembledChannelPriceListType.php @@ -8,6 +8,7 @@ use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints\NotNull; class AssembledChannelPriceListType extends AbstractType { @@ -27,6 +28,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]) ->add('defaultPrice', ProductChannelPriceType::class, [ 'required' => true, + 'allowed_empty_value' => false ]) ->add('specialPrice', ProductChannelPriceType::class, [ 'required' => false, diff --git a/src/Marello/Bundle/PricingBundle/Form/Type/ProductChannelPriceType.php b/src/Marello/Bundle/PricingBundle/Form/Type/ProductChannelPriceType.php index b2bc176f4..045c5292a 100644 --- a/src/Marello/Bundle/PricingBundle/Form/Type/ProductChannelPriceType.php +++ b/src/Marello/Bundle/PricingBundle/Form/Type/ProductChannelPriceType.php @@ -9,6 +9,7 @@ use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints\NotNull; class ProductChannelPriceType extends AbstractType { @@ -28,6 +29,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]) ->add('value', OroMoneyType::class, [ 'required' => false, + 'constraints' => $options['allowed_empty_value'] === false ? new NotNull() : null, 'label' => 'marello.pricing.productprice.value.label', ]); } @@ -41,7 +43,8 @@ public function configureOptions(OptionsResolver $resolver) 'data_class' => ProductChannelPrice::class, 'intention' => 'productchannelprice', 'single_form' => true, - 'excluded_channels' => [] + 'excluded_channels' => [], + 'allowed_empty_value' => true ]); } diff --git a/src/Marello/Bundle/PricingBundle/Formatter/LabelVATAwareFormatter.php b/src/Marello/Bundle/PricingBundle/Formatter/LabelVATAwareFormatter.php index 49113744a..854d5612b 100644 --- a/src/Marello/Bundle/PricingBundle/Formatter/LabelVATAwareFormatter.php +++ b/src/Marello/Bundle/PricingBundle/Formatter/LabelVATAwareFormatter.php @@ -4,7 +4,7 @@ use Marello\Bundle\PricingBundle\DependencyInjection\Configuration; use Oro\Bundle\ConfigBundle\Config\ConfigManager; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class LabelVATAwareFormatter { diff --git a/src/Marello/Bundle/PricingBundle/Model/PricingAwareInterface.php b/src/Marello/Bundle/PricingBundle/Model/PricingAwareInterface.php index d53d3ad7c..a29862f1b 100644 --- a/src/Marello/Bundle/PricingBundle/Model/PricingAwareInterface.php +++ b/src/Marello/Bundle/PricingBundle/Model/PricingAwareInterface.php @@ -5,7 +5,6 @@ use Doctrine\Common\Collections\ArrayCollection; use Marello\Bundle\PricingBundle\Entity\AssembledChannelPriceList; use Marello\Bundle\PricingBundle\Entity\AssembledPriceList; -use Marello\Bundle\PricingBundle\Entity\ProductChannelPrice; interface PricingAwareInterface { diff --git a/src/Marello/Bundle/PricingBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/PricingBundle/Resources/config/jsmodules.yml new file mode 100644 index 000000000..8874ecdc2 --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Resources/config/jsmodules.yml @@ -0,0 +1,4 @@ +dynamic-imports: + marellopricing: + - marellopricing/js/app/views/channel-pricing-item-view + - marellopricing/js/app/views/channel-pricing-items-view \ No newline at end of file diff --git a/src/Marello/Bundle/PricingBundle/Resources/config/oro/twig.yml b/src/Marello/Bundle/PricingBundle/Resources/config/oro/twig.yml new file mode 100644 index 000000000..7532b70f4 --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Resources/config/oro/twig.yml @@ -0,0 +1,2 @@ +bundles: + - MarelloPricingBundle:Form:fields.html.twig diff --git a/src/Marello/Bundle/PricingBundle/Resources/config/services.yml b/src/Marello/Bundle/PricingBundle/Resources/config/services.yml index cefb7c7fb..3fd6760df 100644 --- a/src/Marello/Bundle/PricingBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/PricingBundle/Resources/config/services.yml @@ -1,6 +1,7 @@ services: marello_productprice.pricing.provider.currency_provider: class: Marello\Bundle\PricingBundle\Provider\CurrencyProvider + public: true arguments: - '@doctrine' - '@oro_locale.settings' @@ -64,3 +65,17 @@ services: - '@marello_productprice.pricing.formatter.vat_aware_label' tags: - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-productchannelprice-grid, method: onBuildBefore } + + marello_productprice.pricing.listener.datagrid.products_grid.prices: + class: 'Marello\Bundle\PricingBundle\EventListener\Datagrid\PricesDatagridListener' + arguments: + - 'Marello\Bundle\ProductBundle\Entity\Product' + tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-products-grid, method: onBuildBefore } + + marello_productprice.pricing.listener.datagrid.products_grid.channel_prices: + class: 'Marello\Bundle\PricingBundle\EventListener\Datagrid\ChannelPricesDatagridListener' + arguments: + - 'Marello\Bundle\ProductBundle\Entity\Product' + tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-products-grid, method: onBuildBefore } \ No newline at end of file diff --git a/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-item-view.js b/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-item-view.js index 54fefe60b..d42ff5c6d 100644 --- a/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-item-view.js +++ b/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-item-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var ChannelPricingItemView, + const $ = require('jquery'), _ = require('underscore'), mediator = require('oroui/js/mediator'), @@ -12,7 +12,7 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemView * @class marellopricing.app.views.ChannelPricingItemView */ - ChannelPricingItemView = AbstractItemView.extend({ + const ChannelPricingItemView = AbstractItemView.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-items-view.js b/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-items-view.js index 9b8179372..f6b027f17 100644 --- a/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-items-view.js +++ b/src/Marello/Bundle/PricingBundle/Resources/public/js/app/views/channel-pricing-items-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var PricingItemsView, + const $ = require('jquery'), __ = require('orotranslation/js/translator'), routing = require('routing'), @@ -14,7 +14,7 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemsView * @class marellopricing.app.views.PricingItemsView */ - PricingItemsView = AbstractItemsView.extend({ + const PricingItemsView = AbstractItemsView.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/PricingBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/PricingBundle/Resources/translations/messages.en.yml index bc71b2666..7d22bea85 100644 --- a/src/Marello/Bundle/PricingBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/PricingBundle/Resources/translations/messages.en.yml @@ -45,7 +45,6 @@ marello: No configured channels found for the current product. Please make sure Sales Channels exist and linked. no_prices_configured: No prices configured for current product - type.label: Price Type pricetype: entity_label: Price Type diff --git a/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultChannelPrices.html.twig b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultChannelPrices.html.twig new file mode 100644 index 000000000..63a2d5436 --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultChannelPrices.html.twig @@ -0,0 +1,7 @@ +{% set prices = record.getValue('defaultChannelPrices')|split(';') %} +{% for index, valueSet in prices %} + {% set values = valueSet|split('|') %} + {% if values|length == 3 %} + {{ values[0] }}: {{ values[1]|oro_format_currency({'currency': values[2]}) }}
+ {% endif %} +{% endfor %} diff --git a/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultPrices.html.twig b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultPrices.html.twig new file mode 100644 index 000000000..cac4859ba --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/defaultPrices.html.twig @@ -0,0 +1,7 @@ +{% set prices = record.getValue('defaultPrices')|split(';') %} +{% for index, valueSet in prices %} + {% set values = valueSet|split('|') %} + {% if values|length == 2 %} + {{ values[0]|oro_format_currency({'currency': values[1]}) }}
+ {% endif %} +{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/msrpPrices.html.twig b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/msrpPrices.html.twig new file mode 100644 index 000000000..c2bd44916 --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/msrpPrices.html.twig @@ -0,0 +1,7 @@ +{% set prices = record.getValue('msrpPrices')|split(';') %} +{% for index, valueSet in prices %} + {% set values = valueSet|split('|') %} + {% if values|length == 2 %} + {{ values[0]|oro_format_currency({'currency': values[1]}) }}
+ {% endif %} +{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialChannelPrices.html.twig b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialChannelPrices.html.twig new file mode 100644 index 000000000..b625f13de --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialChannelPrices.html.twig @@ -0,0 +1,7 @@ +{% set prices = record.getValue('specialChannelPrices')|split(';') %} +{% for index, valueSet in prices %} + {% set values = valueSet|split('|') %} + {% if values|length == 3 %} + {{ values[0] }}: {{ values[1]|oro_format_currency({'currency': values[2]}) }}
+ {% endif %} +{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialPrices.html.twig b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialPrices.html.twig new file mode 100644 index 000000000..7791957fb --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Resources/views/Datagrid/Property/specialPrices.html.twig @@ -0,0 +1,7 @@ +{% set prices = record.getValue('specialPrices')|split(';') %} +{% for index, valueSet in prices %} + {% set values = valueSet|split('|') %} + {% if values|length == 2 %} + {{ values[0]|oro_format_currency({'currency': values[1]}) }}
+ {% endif %} +{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/PricingBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/PricingBundle/Resources/views/Form/fields.html.twig index da57a0f41..d21d72418 100644 --- a/src/Marello/Bundle/PricingBundle/Resources/views/Form/fields.html.twig +++ b/src/Marello/Bundle/PricingBundle/Resources/views/Form/fields.html.twig @@ -1,176 +1,3 @@ -{% block marello_product_channel_price_widget %} - -
- {{ form_widget(form.channel) }} -
- {{ form_errors(form.channel) }} - - -
-
- {{ marello_pricing_get_currency_data({'salesChannel':form.channel.vars.value}) }} -
- {{ form_widget(form.currency) }} -
- {{ form_errors(form.currency) }} - - -
- {{ form_widget(form.value) }} -
- {{ form_errors(form.value) }} - -{% endblock %} - -{% macro marello_product_channel_price_collection_item_prototype(widget) %} - {% if 'collection' in widget.vars.block_prefixes %} - {% set form = widget.vars.prototype %} - {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} - {% set disabled = widget.vars.disabled %} - {% set allow_delete = widget.vars.allow_delete %} - {% set allow_add_after = widget.vars.allow_add_after %} - {% else %} - {% set form = widget %} - {% set name = widget.vars.full_name %} - {% set disabled = widget.parent.vars.disabled %} - {% set allow_delete = widget.parent.vars.allow_delete %} - {% set allow_add_after = widget.parent.vars.allow_add_after %} - {% endif %} - - {{ form_widget(form, {disabled: disabled}) }} - {% if allow_delete %} - - - - {% elseif widget.parent.vars.allow_delete %} - - {% endif %} - -{% endmacro %} - -{% block marello_product_channel_price_collection_widget %} - {% spaceless %} - {% if prototype is defined %} - {% set prototype_html = _self.marello_product_channel_price_collection_item_prototype(form) %} - {% endif %} - {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} - {% set id = id ~ '_collection' %} -
- {{ form.parent.pricing_enabled is defined ? form_row(form.parent.pricing_enabled): '' }} - {% set prototype_name = form.vars.prototype_name %} -
- - - - - - - {% if form.vars.allow_delete %} - - {% endif %} - - - - {% if form.children|length %} - {% for child in form.children %} - {{ _self.marello_product_channel_price_collection_item_prototype(child) }} - {% endfor %} - {% elseif show_form_when_empty and prototype_html is defined %} - {{ prototype_html|replace({(prototype_name): '0'})|raw }} - {% endif %} - -
{{ 'marello.sales.saleschannel.entity_label'|trans }}{{ 'marello.pricing.productchannelprice.currency.label'|trans }}{{ marello_pricing_vat_aware_label('marello.pricing.productchannelprice.value.label') }}
- {% if allow_add %} - {{ form.vars.add_label|default('marello.pricing.productchannelprice.form.add_channel_price')|trans }} - {% endif %} -
-
- {% if handle_primary and (prototype is not defined or prototype.primary is defined) %} - {{ _self.oro_collection_validate_primary_js(_context) }} - {% endif %} - {% endspaceless %} -{% endblock %} - -{% block marello_product_price_widget %} - -
-
- {{ marello_pricing_get_currency_data({'currencyCode' : form.currency.vars.value}) }} -
- {{ form_widget(form.currency) }} -
- {{ form_errors(form.currency) }} - - -
- {{ form_widget(form.value) }} -
- {{ form_errors(form.value) }} - -{% endblock %} - -{% macro marello_product_price_collection_item_prototype(widget) %} - {% if 'collection' in widget.vars.block_prefixes %} - {% set form = widget.vars.prototype %} - {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} - {% set disabled = widget.vars.disabled %} - {% set allow_delete = widget.vars.allow_delete %} - {% set allow_add_after = widget.vars.allow_add_after %} - {% else %} - {% set form = widget %} - {% set name = widget.vars.full_name %} - {% set disabled = widget.parent.vars.disabled %} - {% set allow_delete = widget.parent.vars.allow_delete %} - {% set allow_add_after = widget.parent.vars.allow_add_after %} - {% endif %} - - {{ form_widget(form, {disabled: disabled}) }} - -{% endmacro %} - -{% block marello_product_price_collection_widget %} - {% spaceless %} - {% if prototype is defined %} - {% set prototype_html = _self.marello_product_price_collection_item_prototype(form) %} - {% endif %} - {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} - {% set id = id ~ '_collection' %} -
- {% set prototype_name = form.vars.prototype_name %} -
- - - - - - - - - {% if form.children|length %} - {% for child in form.children %} - {{ _self.marello_product_price_collection_item_prototype(child) }} - {% endfor %} - {% elseif show_form_when_empty and prototype_html is defined %} - {{ prototype_html|replace({(prototype_name): '0'})|raw }} - {% endif %} - -
{{ 'marello.pricing.productprice.currency.label'|trans }}{{ marello_pricing_vat_aware_label('marello.pricing.productprice.value.label') }}
-
-
- {% if handle_primary and (prototype is not defined or prototype.primary is defined) %} - {{ _self.oro_collection_validate_primary_js(_context) }} - {% endif %} - {% endspaceless %} -{% endblock %} - {% block marello_assembled_price_list_widget %}
@@ -201,7 +28,7 @@ {% endblock %} -{% macro marello_assembled_price_list_collection_item_prototype(widget) %} +{% macro marello_assembled_price_list_collection_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -215,7 +42,7 @@ {% set allow_delete = widget.parent.vars.allow_delete %} {% set allow_add_after = widget.parent.vars.allow_add_after %} {% endif %} - {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_assembled_price_list_collection_item_prototype(child) }} + {{ fields.marello_assembled_price_list_collection_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -256,7 +86,7 @@
{% if handle_primary and (prototype is not defined or prototype.primary is defined) %} - {{ _self.oro_collection_validate_primary_js(_context) }} + {{ fields.oro_collection_validate_primary_js(_context) }} {% endif %} {% endspaceless %} {% endblock %} @@ -291,7 +121,7 @@ {% endblock %} -{% macro marello_assembled_channel_price_list_collection_item_prototype(widget) %} +{% macro marello_assembled_channel_price_list_collection_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -305,7 +135,7 @@ {% set allow_delete = widget.parent.vars.allow_delete %} {% set allow_add_after = widget.parent.vars.allow_add_after %} {% endif %} - {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_assembled_channel_price_list_collection_item_prototype(child) }} + {{ fields.marello_assembled_channel_price_list_collection_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -360,7 +193,7 @@ {% if handle_primary and (prototype is not defined or prototype.primary is defined) %} - {{ _self.oro_collection_validate_primary_js(_context) }} + {{ fields.oro_collection_validate_primary_js(_context) }} {% endif %} {% endspaceless %} {% endblock %} diff --git a/src/Marello/Bundle/PricingBundle/Subtotal/Provider/AbstractSubtotalProvider.php b/src/Marello/Bundle/PricingBundle/Subtotal/Provider/AbstractSubtotalProvider.php index 4e9d72960..b89439d3f 100644 --- a/src/Marello/Bundle/PricingBundle/Subtotal/Provider/AbstractSubtotalProvider.php +++ b/src/Marello/Bundle/PricingBundle/Subtotal/Provider/AbstractSubtotalProvider.php @@ -6,7 +6,7 @@ use Marello\Bundle\PricingBundle\Subtotal\Model\Subtotal; use Oro\Bundle\CurrencyBundle\Provider\DefaultCurrencyProviderInterface; use Oro\Bundle\CurrencyBundle\Rounding\RoundingServiceInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; abstract class AbstractSubtotalProvider implements SubtotalProviderInterface { diff --git a/src/Marello/Bundle/PricingBundle/Tests/Functional/Api/requests/product_without_prices.yml b/src/Marello/Bundle/PricingBundle/Tests/Functional/Api/requests/product_without_prices.yml index 32883693b..b98c63b52 100644 --- a/src/Marello/Bundle/PricingBundle/Tests/Functional/Api/requests/product_without_prices.yml +++ b/src/Marello/Bundle/PricingBundle/Tests/Functional/Api/requests/product_without_prices.yml @@ -1,9 +1,12 @@ data: type: marelloproducts id: 'my-sku' - attributes: - name: 'My name' relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'names-1' saleschannels: data: - @@ -17,3 +20,14 @@ data: data: type: marellotaxcodes id: 'taxCode->code)>' +included: + - + type: localizedfallbackvalues + id: 'names-1' + attributes: + fallback: null + string: 'My name' + text: null + relationships: + localization: + data: null diff --git a/src/Marello/Bundle/PricingBundle/Tests/Functional/Controller/PricingControllerTest.php b/src/Marello/Bundle/PricingBundle/Tests/Functional/Controller/PricingControllerTest.php index 6b2fe10cf..23267c20c 100644 --- a/src/Marello/Bundle/PricingBundle/Tests/Functional/Controller/PricingControllerTest.php +++ b/src/Marello/Bundle/PricingBundle/Tests/Functional/Controller/PricingControllerTest.php @@ -2,13 +2,10 @@ namespace Marello\Bundle\PricingBundle\Tests\Functional\Controller; -use Symfony\Component\HttpFoundation\Response; - -use Oro\Bundle\TestFrameworkBundle\Test\WebTestCase; - -use Marello\Bundle\SalesBundle\Tests\Functional\DataFixtures\LoadSalesData; -use Marello\Bundle\ProductBundle\Tests\Functional\DataFixtures\LoadProductData; use Marello\Bundle\PricingBundle\Tests\Functional\DataFixtures\LoadProductChannelPricingData; +use Marello\Bundle\SalesBundle\Tests\Functional\DataFixtures\LoadSalesData; +use Oro\Bundle\TestFrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpFoundation\Response; /** * @outputBuffering enabled diff --git a/src/Marello/Bundle/PricingBundle/Tests/Unit/Formatter/LabelVATAwareFormatterTest.php b/src/Marello/Bundle/PricingBundle/Tests/Unit/Formatter/LabelVATAwareFormatterTest.php index 4fc857c6c..65143db5e 100644 --- a/src/Marello/Bundle/PricingBundle/Tests/Unit/Formatter/LabelVATAwareFormatterTest.php +++ b/src/Marello/Bundle/PricingBundle/Tests/Unit/Formatter/LabelVATAwareFormatterTest.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\PricingBundle\Tests\Unit\Formatter; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use PHPUnit\Framework\TestCase; diff --git a/src/Marello/Bundle/PricingBundle/Tests/Unit/Provider/ChannelPriceProviderTest.php b/src/Marello/Bundle/PricingBundle/Tests/Unit/Provider/ChannelPriceProviderTest.php index 319a27163..e0e030e0c 100644 --- a/src/Marello/Bundle/PricingBundle/Tests/Unit/Provider/ChannelPriceProviderTest.php +++ b/src/Marello/Bundle/PricingBundle/Tests/Unit/Provider/ChannelPriceProviderTest.php @@ -4,26 +4,21 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository; - -use PHPUnit\Framework\TestCase; - -use Symfony\Component\Form\FormInterface; - -use Oro\Component\Testing\Unit\EntityTrait; - -use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\SalesBundle\Entity\SalesChannel; -use Marello\Bundle\PricingBundle\Entity\ProductPrice; use Marello\Bundle\LayoutBundle\Context\FormChangeContext; +use Marello\Bundle\LayoutBundle\Context\FormChangeContextInterface; +use Marello\Bundle\OrderBundle\Entity\Order; +use Marello\Bundle\OrderBundle\Provider\OrderItem\OrderItemFormChangesProvider; +use Marello\Bundle\PricingBundle\Entity\AssembledChannelPriceList; use Marello\Bundle\PricingBundle\Entity\AssembledPriceList; use Marello\Bundle\PricingBundle\Entity\ProductChannelPrice; +use Marello\Bundle\PricingBundle\Entity\ProductPrice; use Marello\Bundle\PricingBundle\Provider\ChannelPriceProvider; -use Marello\Bundle\PricingBundle\Entity\AssembledChannelPriceList; -use Marello\Bundle\LayoutBundle\Context\FormChangeContextInterface; +use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\ProductBundle\Entity\Repository\ProductRepository; -use Marello\Bundle\OrderBundle\Provider\OrderItem\OrderItemFormChangesProvider; -use Marello\Bundle\PricingBundle\Entity\Repository\ProductChannelPriceRepository; +use Marello\Bundle\SalesBundle\Entity\SalesChannel; +use Oro\Component\Testing\Unit\EntityTrait; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Form\FormInterface; class ChannelPriceProviderTest extends TestCase { diff --git a/src/Marello/Bundle/PricingBundle/Tests/Unit/Subtotal/Provider/CompositeSubtotalProviderTest.php b/src/Marello/Bundle/PricingBundle/Tests/Unit/Subtotal/Provider/CompositeSubtotalProviderTest.php index b8e301e3d..8cabfafd5 100644 --- a/src/Marello/Bundle/PricingBundle/Tests/Unit/Subtotal/Provider/CompositeSubtotalProviderTest.php +++ b/src/Marello/Bundle/PricingBundle/Tests/Unit/Subtotal/Provider/CompositeSubtotalProviderTest.php @@ -4,7 +4,7 @@ use Doctrine\Common\Collections\ArrayCollection; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use PHPUnit\Framework\TestCase; diff --git a/src/Marello/Bundle/PricingBundle/Twig/PricingExtension.php b/src/Marello/Bundle/PricingBundle/Twig/PricingExtension.php index e3c2d0581..7d02b1f0c 100644 --- a/src/Marello/Bundle/PricingBundle/Twig/PricingExtension.php +++ b/src/Marello/Bundle/PricingBundle/Twig/PricingExtension.php @@ -4,8 +4,10 @@ use Marello\Bundle\PricingBundle\Formatter\LabelVATAwareFormatter; use Marello\Bundle\PricingBundle\Provider\CurrencyProvider; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class PricingExtension extends \Twig_Extension +class PricingExtension extends AbstractExtension { const NAME = 'marello_pricing'; @@ -47,11 +49,11 @@ public function getName() public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_pricing_get_currency_data', [$this, 'getCurrencyData'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_pricing_vat_aware_label', [$this->vatLabelFormatter, 'getFormattedLabel'] ), diff --git a/src/Marello/Bundle/ProductBundle/Async/ProductsAssignSalesChannelsProcessor.php b/src/Marello/Bundle/ProductBundle/Async/ProductsAssignSalesChannelsProcessor.php index 5da959200..7b42f6c94 100644 --- a/src/Marello/Bundle/ProductBundle/Async/ProductsAssignSalesChannelsProcessor.php +++ b/src/Marello/Bundle/ProductBundle/Async/ProductsAssignSalesChannelsProcessor.php @@ -3,20 +3,24 @@ namespace Marello\Bundle\ProductBundle\Async; use Doctrine\ORM\EntityManagerInterface; -use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\SalesBundle\Entity\SalesChannel; -use Oro\Bundle\DataGridBundle\Datagrid\Manager; -use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; -use Oro\Bundle\EmailBundle\Form\Model\Factory; -use Oro\Bundle\EmailBundle\Mailer\Processor; + +use Psr\Log\LoggerInterface; + +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + use Oro\Bundle\UserBundle\Entity\User; -use Oro\Component\MessageQueue\Client\TopicSubscriberInterface; -use Oro\Component\MessageQueue\Consumption\MessageProcessorInterface; +use Oro\Component\MessageQueue\Util\JSON; +use Oro\Bundle\EmailBundle\Mailer\Processor; +use Oro\Bundle\EmailBundle\Form\Model\Factory; +use Oro\Bundle\DataGridBundle\Datagrid\Manager; use Oro\Component\MessageQueue\Transport\MessageInterface; use Oro\Component\MessageQueue\Transport\SessionInterface; -use Oro\Component\MessageQueue\Util\JSON; -use Psr\Log\LoggerInterface; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Oro\Component\MessageQueue\Client\TopicSubscriberInterface; +use Oro\Component\MessageQueue\Consumption\MessageProcessorInterface; + +use Marello\Bundle\ProductBundle\Entity\Product; +use Marello\Bundle\SalesBundle\Entity\SalesChannel; +use Marello\Bundle\ProductBundle\Entity\Repository\ProductRepository; class ProductsAssignSalesChannelsProcessor implements MessageProcessorInterface, TopicSubscriberInterface { @@ -81,53 +85,33 @@ public function __construct( /** * {@inheritdoc} */ - public static function getSubscribedTopics() + public static function getSubscribedTopics(): array { return [self::TOPIC]; } /** - * {@inheritdoc} + * @param MessageInterface $message + * @param SessionInterface $session + * @return string */ - public function process(MessageInterface $message, SessionInterface $session) + public function process(MessageInterface $message, SessionInterface $session): string { $data = JSON::decode($message->getBody()); - $inset = $data['inset']; $products = $data['products']; - $filters = $data['filters']; $salesChannels = $data['salesChannels']; + $salesChannels = $this->entityManager->getRepository(SalesChannel::class)->findBy(['id' => $salesChannels]); - $isAllSelected = $inset === '0'; - $salesChannels = $this->entityManager->getRepository(SalesChannel::class)->findBy(['code' => $salesChannels]); - - if (!empty($products) || $isAllSelected) { - $grid = $this->datagridManager->getDatagridByRequestParams( - 'marello-products-grid', - ['_filter' => $filters] - ); - /** @var OrmDatasource $dataSource */ - $dataSource = $grid->getAcceptedDatasource(); - $queryBuilder = $dataSource->getQueryBuilder(); - - if (!$isAllSelected) { - $queryBuilder->andWhere($queryBuilder->expr()->in('p.id', $products)); - } elseif ($products) { - $queryBuilder->andWhere($queryBuilder->expr()->notIn('p.id', $products)); - } - - $result = $queryBuilder - ->getQuery() - ->setFirstResult(0) - ->setMaxResults(null) - ->getResult(); - + if (!empty($products) && !empty($salesChannels)) { + /** @var ProductRepository $productRepo */ + $productRepo = $this->entityManager->getRepository(Product::class); + $products = $productRepo->findBy(['id' => $products]); try { $iteration = 1; $modifiedProducts = []; - foreach ($result as $entity) { - /** @var Product $entity */ - $entity = $entity[0]; + /** @var Product $entity */ + foreach ($products as $entity) { $addedChannels = 0; foreach ($salesChannels as $salesChannel) { if (!$entity->hasChannel($salesChannel)) { @@ -143,9 +127,6 @@ public function process(MessageInterface $message, SessionInterface $session) if (($iteration % self::FLUSH_BATCH_SIZE) === 0) { $this->entityManager->flush(); } - if ($addedChannels > 0) { - $iteration++; - } } $this->entityManager->flush(); $this->sendMail($salesChannels, $modifiedProducts); @@ -165,10 +146,11 @@ public function process(MessageInterface $message, SessionInterface $session) } /** - * @param SalesChannel[] $assignedSalesChannels - * @param Product[] $modifiedProducts + * @param array $assignedSalesChannels + * @param array $modifiedProducts + * @throws \Swift_SwiftException */ - private function sendMail(array $assignedSalesChannels, array $modifiedProducts) + private function sendMail(array $assignedSalesChannels, array $modifiedProducts): void { /** @var User $currentUser */ $currentUser = $this->tokenStorage->getToken()->getUser(); diff --git a/src/Marello/Bundle/ProductBundle/Controller/ProductController.php b/src/Marello/Bundle/ProductBundle/Controller/ProductController.php index dc322e3a5..bc9f34b06 100644 --- a/src/Marello/Bundle/ProductBundle/Controller/ProductController.php +++ b/src/Marello/Bundle/ProductBundle/Controller/ProductController.php @@ -10,22 +10,26 @@ use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily; use Oro\Bundle\SecurityBundle\Annotation as Security; use Oro\Bundle\UIBundle\Route\Router; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; -class ProductController extends Controller +class ProductController extends AbstractController { const ACTION_SAVE_AND_DUPLICATE = 'save_and_duplicate'; /** - * @Config\Route("/", name="marello_product_index") + * @Route( + * path="/", + * name="marello_product_index" + * ) * @AclAncestor("marello_product_view") - * @Config\Template + * @Template */ public function indexAction() { @@ -33,9 +37,12 @@ public function indexAction() } /** - * @Config\Route("/create", name="marello_product_create") + * @Route( + * path="/create", + * name="marello_product_create" + * ) * @AclAncestor("marello_product_create") - * @Config\Template("MarelloProductBundle:Product:createStepOne.html.twig") + * @Template("MarelloProductBundle:Product:createStepOne.html.twig") * * @param Request $request * @@ -55,11 +62,15 @@ protected function createStepOne(Request $request) $form = $this->createForm(ProductStepOneType::class); $handler = new ProductCreateStepOneHandler($form, $request); $productTypesProvider = $this->get('marello_product.provider.product_types'); - + $em = $this->get('doctrine.orm.entity_manager'); + /** @var AttributeFamily $attributeFamily */ + $attributeFamilies = $em + ->getRepository(AttributeFamily::class) + ->findBy(['entityClass' => Product::class]); if ($handler->process()) { return $this->forward('MarelloProductBundle:Product:createStepTwo'); } - if (count($productTypesProvider->getProductTypes()) === 1) { + if (count($productTypesProvider->getProductTypes()) <= 1 && count($attributeFamilies) <= 1) { $request->setMethod('POST'); $request->request->set('input_action', 'marello_product_create'); $request->request->set('single_product_type', true); @@ -73,9 +84,12 @@ protected function createStepOne(Request $request) } /** - * @Config\Route("/create/step-two", name="marello_product_create_step_two") + * @Route( + * path="/create/step-two", + * name="marello_product_create_step_two" + * ) * - * @Config\Template("MarelloProductBundle:Product:createStepTwo.html.twig") + * @Template("MarelloProductBundle:Product:createStepTwo.html.twig") * * @AclAncestor("marello_product_create") * @@ -96,43 +110,40 @@ protected function createStepTwo(Request $request, Product $product) { if ($request->get('input_action') === 'marello_product_create') { $formStepOne = $this->createForm(ProductStepOneType::class, $product); + $em = $this->get('doctrine.orm.entity_manager'); if ($request->get('single_product_type')) { $type = Product::DEFAULT_PRODUCT_TYPE; + $attributeFamily = $em + ->getRepository(AttributeFamily::class) + ->findOneBy(['entityClass' => Product::class]); } else { $formStepOne->handleRequest($request); $type = $formStepOne->get('type')->getData(); + $attributeFamily = $formStepOne->get('attributeFamily')->getData(); } $product->setType($type); - $productTypesProvider = $this->get('marello_product.provider.product_types'); - $em = $this->get('doctrine.orm.entity_manager'); - $productType = $productTypesProvider->getProductType($type); - if ($productType) { - /** @var AttributeFamily $attributeFamily */ - $attributeFamily = $em - ->getRepository(AttributeFamily::class) - ->findOneBy(['code' => $productType->getAttributeFamilyCode()]); - $product->setAttributeFamily($attributeFamily); + $product->setAttributeFamily($attributeFamily); + + $form = $this->createForm(ProductType::class, $product); - $form = $this->createForm(ProductType::class, $product); - $form->get('type')->setData($type); - $form->get('attributeFamily')->setData($attributeFamily->getId()); - } return [ 'form' => $form->createView(), 'entity' => $product, 'isWidgetContext' => (bool)$request->get('_wid', false) ]; } - //$form = $this->createForm(ProductStepOneType::class, $product, ['validation_groups'=> false]); - //$form->submit($request->request->get(ProductType::BLOCK_PREFIX)); return $this->update($product, $request); } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_product_update") + * @Route( + * path="/update/{id}", + * requirements={"id"="\d+"}, + * name="marello_product_update" + * ) * @AclAncestor("marello_product_update") - * @Config\Template + * @Template * * @param Product $product * @param Request $request @@ -200,9 +211,13 @@ protected function update(Product $product, Request $request) } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_product_view") + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_product_view" + * ) * @AclAncestor("marello_product_view") - * @Config\Template("MarelloProductBundle:Product:view.html.twig") + * @Template("MarelloProductBundle:Product:view.html.twig") * * @param Product $product * @@ -216,9 +231,13 @@ public function viewAction(Product $product) } /** - * @Config\Route("/widget/info/{id}", name="marello_product_widget_info", requirements={"id"="\d+"}) + * @Route( + * path="/widget/info/{id}", + * name="marello_product_widget_info", + * requirements={"id"="\d+"} + * ) * @AclAncestor("marello_product_view") - * @Config\Template + * @Template("MarelloProductBundle:Product/widget:info.html.twig") * * @param Product $product * @@ -232,9 +251,13 @@ public function infoAction(Product $product) } /** - * @Config\Route("/widget/price/{id}", name="marello_product_widget_price", requirements={"id"="\d+"}) + * @Route( + * path="/widget/price/{id}", + * name="marello_product_widget_price", + * requirements={"id"="\d+"} + * ) * @AclAncestor("marello_product_view") - * @Config\Template + * @Template("MarelloProductBundle:Product/widget:price.html.twig") * * @param Product $product * @@ -248,9 +271,12 @@ public function priceAction(Product $product) } /** - * @Config\Route("/assign-sales-channels", name="marello_product_assign_sales_channels") + * @Route( + * path="/assign-sales-channels", + * name="marello_product_assign_sales_channels" + * ) * @AclAncestor("marello_product_update") - * @Config\Template + * @Template * * @return array */ diff --git a/src/Marello/Bundle/ProductBundle/Controller/VariantController.php b/src/Marello/Bundle/ProductBundle/Controller/VariantController.php index 9b3d52e0b..78de2c514 100644 --- a/src/Marello/Bundle/ProductBundle/Controller/VariantController.php +++ b/src/Marello/Bundle/ProductBundle/Controller/VariantController.php @@ -2,25 +2,29 @@ namespace Marello\Bundle\ProductBundle\Controller; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; +use Symfony\Component\Routing\Annotation\Route; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\ProductBundle\Entity\Variant; -class VariantController extends Controller +class VariantController extends AbstractController { /** - * @Config\Route("/create/parent/{id}", requirements={"id"="\d+"}, name="marello_product_create_variant") - * @Config\Method({"GET", "POST"}) + * @Route( + * path="/create/parent/{id}", + * methods={"GET", "POST"}, + * requirements={"id"="\d+"}, + * name="marello_product_create_variant" + * ) * @AclAncestor("marello_product_create_variant") - * @Config\Template("MarelloProductBundle:Variant:update.html.twig") + * @Template("MarelloProductBundle:Variant:update.html.twig") * * @param Product $product * @return array @@ -31,12 +35,12 @@ public function createVariantAction(Product $product) } /** - * @Config\Route( - * "/add/{id}/parent/{parentId}", + * @Route( + * path="/add/{id}/parent/{parentId}", * requirements={"id"="\d+","parentId"="\d+"}, name="marello_product_add_variant" * ) * @AclAncestor("marello_product_add_variant") - * @Config\Template("MarelloProductBundle:Variant:update.html.twig") + * @Template("MarelloProductBundle:Variant:update.html.twig") * * @param Request $request * @param Variant $variant @@ -54,7 +58,7 @@ public function updateVariantAction(Request $request, Variant $variant) $entityClass = $this->get('oro_entity.routing_helper')->resolveEntityClass($entityClass); $parentId = $request->get('parentId'); - if ($parentId && $entityClass === $this->container->getParameter('marello_product.entity.class')) { + if ($parentId && $entityClass === Product::class) { $repository = $this->getDoctrine()->getRepository($entityClass); /** @var Product $parent */ $parent = $repository->find($parentId); @@ -108,9 +112,9 @@ protected function updateVariant(Product $product, Variant $variant) } /** - * @Config\Route("/widget/info/{id}", name="marello_product_variant_widget_info", requirements={"id"="\d+"}) + * @Route(path="/widget/info/{id}", name="marello_product_variant_widget_info", requirements={"id"="\d+"}) * @AclAncestor("marello_product_view") - * @Config\Template + * @Template * * @param Product $product * @return array diff --git a/src/Marello/Bundle/ProductBundle/Duplicator/ProductDuplicator.php b/src/Marello/Bundle/ProductBundle/Duplicator/ProductDuplicator.php index 9b6eef8d0..56d8a95ec 100755 --- a/src/Marello/Bundle/ProductBundle/Duplicator/ProductDuplicator.php +++ b/src/Marello/Bundle/ProductBundle/Duplicator/ProductDuplicator.php @@ -103,10 +103,10 @@ protected function createProductCopy(Product $product) $productCopy = clone $product; $baseSku = $this->defineBaseValue($product->getSku(), 'sku'); $newSku = $this->skuIncrementor->increment($baseSku); - $baseName = $this->defineBaseValue($product->getName(), 'name'); + $baseName = $this->defineBaseValue($product->getDenormalizedDefaultName(), 'denormalizedDefaultName'); $productCopy ->setSku($newSku) - ->setName(sprintf('%s-%s', $baseName, substr($newSku, strlen($baseSku) + 1))); + ->setDefaultName(sprintf('%s-%s', $baseName, substr($newSku, strlen($baseSku) + 1))); $disabledStatus = $this->doctrineHelper ->getEntityManagerForClass(ProductStatus::class) ->getRepository(ProductStatus::class) diff --git a/src/Marello/Bundle/ProductBundle/Entity/Manager/ProductApiEntityManager.php b/src/Marello/Bundle/ProductBundle/Entity/Manager/ProductApiEntityManager.php index e1f0b79c1..fad3afb87 100644 --- a/src/Marello/Bundle/ProductBundle/Entity/Manager/ProductApiEntityManager.php +++ b/src/Marello/Bundle/ProductBundle/Entity/Manager/ProductApiEntityManager.php @@ -12,7 +12,7 @@ protected function getSerializationConfig() 'exclusion_policy' => 'all', 'fields' => [ 'id' => null, - 'name' => null, + 'names' => null, 'sku' => null, 'status' => [ 'exclusion_policy' => 'all', diff --git a/src/Marello/Bundle/ProductBundle/Entity/Product.php b/src/Marello/Bundle/ProductBundle/Entity/Product.php index 067c68151..e9eb2a9bb 100644 --- a/src/Marello/Bundle/ProductBundle/Entity/Product.php +++ b/src/Marello/Bundle/ProductBundle/Entity/Product.php @@ -5,14 +5,6 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; - -use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; -use Oro\Bundle\OrganizationBundle\Entity\Organization; -use Oro\Bundle\OrganizationBundle\Entity\OrganizationAwareInterface; -use Oro\Bundle\OrganizationBundle\Entity\OrganizationInterface; -use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily; -use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamilyAwareInterface; - use Marello\Bundle\CatalogBundle\Entity\Category; use Marello\Bundle\InventoryBundle\Entity\InventoryItem; use Marello\Bundle\InventoryBundle\Model\InventoryItemAwareInterface; @@ -22,13 +14,18 @@ use Marello\Bundle\PricingBundle\Model\PricingAwareInterface; use Marello\Bundle\ProductBundle\Model\ExtendProduct; use Marello\Bundle\SalesBundle\Entity\SalesChannel; -use Marello\Bundle\SalesBundle\Model\SalesChannelAwareInterface; +use Marello\Bundle\SalesBundle\Model\SalesChannelsAwareInterface; use Marello\Bundle\SupplierBundle\Entity\Supplier; use Marello\Bundle\TaxBundle\Entity\TaxCode; +use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily; +use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamilyAwareInterface; +use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; +use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue; +use Oro\Bundle\OrganizationBundle\Entity\Organization; +use Oro\Bundle\OrganizationBundle\Entity\OrganizationAwareInterface; +use Oro\Bundle\OrganizationBundle\Entity\OrganizationInterface; /** - * Represents a Marello Product - * * @ORM\Entity(repositoryClass="Marello\Bundle\ProductBundle\Entity\Repository\ProductRepository") * @ORM\Table( * name="marello_product_product", @@ -69,7 +66,7 @@ */ class Product extends ExtendProduct implements ProductInterface, - SalesChannelAwareInterface, + SalesChannelsAwareInterface, PricingAwareInterface, OrganizationAwareInterface, InventoryItemAwareInterface, @@ -92,23 +89,62 @@ class Product extends ExtendProduct implements * ) */ protected $id; - + /** + * This is a mirror field for performance reasons only. + * It mirrors getDefaultName()->getString(). + * * @var string * - * @ORM\Column(name="name", type="string", nullable=false) + * @ORM\Column(name="name", type="string", length=255, nullable=false) + * @Oro\ConfigField( + * defaultValues={ + * "importexport"={ + * "excluded"=true + * } + * }, + * mode="hidden" + * ) + */ + protected $denormalizedDefaultName; + + /** + * @var Collection|LocalizedFallbackValue[] + * + * @ORM\ManyToMany( + * targetEntity="Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue", + * cascade={"ALL"}, + * orphanRemoval=true + * ) + * @ORM\JoinTable( + * name="marello_product_product_name", + * joinColumns={ + * @ORM\JoinColumn(name="product_id", referencedColumnName="id", onDelete="CASCADE") + * }, + * inverseJoinColumns={ + * @ORM\JoinColumn(name="localized_value_id", referencedColumnName="id", onDelete="CASCADE", unique=true) + * } + * ) * @Oro\ConfigField( * defaultValues={ * "dataaudit"={ * "auditable"=true * }, * "importexport"={ - * "excluded"=true + * "order"=20, + * "full"=true, + * "fallback_field"="string" + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) */ - protected $name; + protected $names; /** * @var string @@ -123,6 +159,12 @@ class Product extends ExtendProduct implements * "order"=10, * "header"="SKU", * "identity"=true, + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) @@ -140,6 +182,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="Custom" * } * } * ) @@ -158,6 +206,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) @@ -181,23 +235,6 @@ class Product extends ExtendProduct implements */ protected $type; - /** - * @var double - * - * @ORM\Column(name="cost", type="money", nullable=true) - * @Oro\ConfigField( - * defaultValues={ - * "dataaudit"={ - * "auditable"=true - * }, - * "importexport"={ - * "excluded"=true - * } - * } - * ) - */ - protected $cost; - /** * @ORM\Column(type="float", nullable=true) * @Oro\ConfigField( @@ -207,6 +244,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="Custom" * } * } * ) @@ -226,6 +269,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="Custom" * } * } * ) @@ -267,6 +316,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) @@ -290,6 +345,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) @@ -310,6 +371,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) @@ -387,6 +454,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="Custom" * } * } * ) @@ -422,6 +495,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) @@ -444,6 +523,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="System" * } * } * ) @@ -466,6 +551,12 @@ class Product extends ExtendProduct implements * }, * "importexport"={ * "excluded"=true + * }, + * "attribute"={ + * "is_attribute"=true + * }, + * "extend"={ + * "owner"="Custom" * } * } * ) @@ -490,8 +581,37 @@ class Product extends ExtendProduct implements */ protected $attributeFamily; + /** + * @var \DateTime + * + * @ORM\Column(name="created_at", type="datetime") + * @Oro\ConfigField( + * defaultValues={ + * "entity"={ + * "label"="oro.ui.created_at" + * } + * } + * ) + */ + protected $createdAt; + + /** + * @var \DateTime + * + * @ORM\Column(name="updated_at", type="datetime", nullable=true) + * @Oro\ConfigField( + * defaultValues={ + * "entity"={ + * "label"="oro.ui.updated_at" + * } + * } + * ) + */ + protected $updatedAt; + public function __construct() { + $this->names = new ArrayCollection(); $this->prices = new ArrayCollection(); $this->channelPrices = new ArrayCollection(); $this->channels = new ArrayCollection(); @@ -505,6 +625,7 @@ public function __clone() { if ($this->id) { $this->id = null; + $this->names = new ArrayCollection(); $this->prices = new ArrayCollection(); $this->channelPrices = new ArrayCollection(); $this->channels = new ArrayCollection(); @@ -523,21 +644,53 @@ public function getId() } /** - * @return string + * @param array|LocalizedFallbackValue[] $names + * + * @return $this + */ + public function setNames(array $names = []) + { + $this->names->clear(); + + foreach ($names as $name) { + $this->addName($name); + } + + return $this; + } + + /** + * @return Collection|LocalizedFallbackValue[] */ - public function getName() + public function getNames() { - return $this->name; + return $this->names; } /** - * @param string $name + * @param LocalizedFallbackValue $name * - * @return Product + * @return $this + */ + public function addName(LocalizedFallbackValue $name) + { + if (!$this->names->contains($name)) { + $this->names->add($name); + } + + return $this; + } + + /** + * @param LocalizedFallbackValue $name + * + * @return $this */ - public function setName($name) + public function removeName(LocalizedFallbackValue $name) { - $this->name = $name; + if ($this->names->contains($name)) { + $this->names->removeElement($name); + } return $this; } @@ -835,13 +988,21 @@ public function getData() { return $this->data; } - + /** * @return string */ public function __toString() { - return $this->name; + try { + if ($this->getDefaultName()) { + return (string) $this->getDefaultName(); + } else { + return (string) $this->sku; + } + } catch (\LogicException $e) { + return (string) $this->sku; + } } /** @@ -989,22 +1150,6 @@ public function setPreferredSupplier(Supplier $preferredSupplier) return $this; } - /** - * @ORM\PreUpdate - */ - public function preUpdateTimestamp() - { - $this->updatedAt = new \DateTime('now', new \DateTimeZone('UTC')); - } - - /** - * @ORM\PrePersist - */ - public function prePersistTimestamp() - { - $this->createdAt = $this->updatedAt = new \DateTime('now', new \DateTimeZone('UTC')); - } - /** * Set taxCode * @@ -1198,4 +1343,82 @@ public function setType($type) return $this; } + + /** + * This field is read-only, updated automatically prior to persisting. + * + * @return string + */ + public function getDenormalizedDefaultName() + { + return $this->denormalizedDefaultName; + } + + /** + * Set createdAt + * + * @param \DateTime $createdAt + * + * @return mixed + */ + public function setCreatedAt($createdAt) + { + $this->createdAt = $createdAt; + + return $this; + } + + /** + * @return \Datetime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * Set updatedAt + * + * @param \DateTime $updatedAt + * + * @return mixed + */ + public function setUpdatedAt($updatedAt) + { + $this->updatedAt = $updatedAt; + + return $this; + } + + /** + * @return \Datetime + */ + public function getUpdatedAt() + { + return $this->updatedAt; + } + + /** + * @ORM\PrePersist + */ + public function prePersist() + { + $this->createdAt = new \DateTime('now', new \DateTimeZone('UTC')); + if (!$this->getDefaultName()) { + throw new \RuntimeException('Product has to have a default name'); + } + $this->denormalizedDefaultName = $this->getDefaultName()->getString(); + } + + /** + * @ORM\PreUpdate + */ + public function preUpdate() + { + $this->updatedAt = new \DateTime('now', new \DateTimeZone('UTC')); + if (!$this->getDefaultName()) { + throw new \RuntimeException('Product has to have a default name'); + } + $this->denormalizedDefaultName = $this->getDefaultName()->getString(); + } } diff --git a/src/Marello/Bundle/ProductBundle/Entity/ProductChannelTaxRelation.php b/src/Marello/Bundle/ProductBundle/Entity/ProductChannelTaxRelation.php index 290f52e98..3bb27dcf0 100644 --- a/src/Marello/Bundle/ProductBundle/Entity/ProductChannelTaxRelation.php +++ b/src/Marello/Bundle/ProductBundle/Entity/ProductChannelTaxRelation.php @@ -3,10 +3,13 @@ namespace Marello\Bundle\ProductBundle\Entity; use Doctrine\ORM\Mapping as ORM; -use Marello\Bundle\SalesBundle\Entity\SalesChannel; -use Marello\Bundle\TaxBundle\Entity\TaxCode; + use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; +use Marello\Bundle\TaxBundle\Entity\TaxCode; +use Marello\Bundle\SalesBundle\Entity\SalesChannel; +use Marello\Bundle\SalesBundle\Model\SalesChannelAwareInterface; + /** * TaxCode * @@ -22,7 +25,7 @@ * ) * @Oro\Config() */ -class ProductChannelTaxRelation +class ProductChannelTaxRelation implements SalesChannelAwareInterface { /** * @var integer diff --git a/src/Marello/Bundle/ProductBundle/EventListener/AttributeFormViewListener.php b/src/Marello/Bundle/ProductBundle/EventListener/AttributeFormViewListener.php new file mode 100644 index 000000000..64c7fb877 --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/EventListener/AttributeFormViewListener.php @@ -0,0 +1,298 @@ +attributeManager = $attributeManager; + } + + /** + * @param BeforeListRenderEvent $event + */ + public function onEdit(BeforeListRenderEvent $event) + { + $entity = $event->getEntity(); + + if (!$entity instanceof AttributeFamilyAwareInterface) { + return; + } + + $scrollData = $event->getScrollData(); + $formView = $event->getFormView(); + $groupsData = $this->attributeManager->getGroupsWithAttributes($entity->getAttributeFamily()); + $this->filterGroupAttributes($groupsData, 'form', 'is_enabled'); + $this->addNotEmptyGroupBlocks($scrollData, $groupsData); + + foreach ($groupsData as $groupsDatum) { + /** @var AttributeGroup $group */ + $group = $groupsDatum['group']; + /** @var FieldConfigModel $attribute */ + foreach ($groupsDatum['attributes'] as $attribute) { + $fieldId = $attribute->getFieldName(); + if (in_array($fieldId, $this->getRestrictedToMoveFields(), true)) { + continue; + } + $attributeView = $formView->offsetGet($fieldId); + + if (!$attributeView->isRendered()) { + $html = $event->getEnvironment()->render('OroEntityConfigBundle:Attribute:row.html.twig', [ + 'child' => $attributeView, + ]); + + $subblockId = $scrollData->addSubBlock($group->getCode()); + $scrollData->addSubBlockData($group->getCode(), $subblockId, $html, $fieldId); + } else { + $this->moveFieldToBlock($scrollData, $attribute->getFieldName(), $group->getCode()); + } + } + } + + $this->combineGroupBlocks($scrollData); + $this->removeEmptyGroupBlocks($scrollData); + } + + /** + * @param ScrollData $scrollData + */ + private function combineGroupBlocks(ScrollData $scrollData) + { + $data = $scrollData->getData(); + if (empty($data[ScrollData::DATA_BLOCKS])) { + return; + } + $notAttributesGroupBlocksByIds = []; + $notAttributesGroupBlocksByTitles = []; + foreach ($data[ScrollData::DATA_BLOCKS] as $blockId => $blockData) { + if (!is_string($blockId)) { + $notAttributesGroupBlocksByIds[$blockId] = $blockData; + $notAttributesGroupBlocksByTitles[$blockData[ScrollData::TITLE]] = $blockId; + } + } + foreach ($data[ScrollData::DATA_BLOCKS] as $blockId => $data) { + if (!is_string($blockId)) { + continue; + } + $isEmpty = true; + if (!empty($data[ScrollData::SUB_BLOCKS])) { + if (isset($notAttributesGroupBlocksByTitles[$data[ScrollData::TITLE]])) { + foreach ($data[ScrollData::SUB_BLOCKS] as $subblockId => $subblockData) { + if (!empty($subblockData[ScrollData::DATA])) { + foreach ($subblockData[ScrollData::DATA] as $fieldName => $fieldData) { + $this->moveFieldToBlock( + $scrollData, + $fieldName, + $notAttributesGroupBlocksByTitles[$data[ScrollData::TITLE]] + ); + } + } + } + } else { + $isEmpty = false; + } + } + + if ($isEmpty) { + $scrollData->removeNamedBlock($blockId); + } + } + } + + /** + * @param ScrollData $scrollData + */ + private function removeEmptyGroupBlocks(ScrollData $scrollData) + { + $data = $scrollData->getData(); + if (empty($data[ScrollData::DATA_BLOCKS])) { + return; + } + + foreach ($data[ScrollData::DATA_BLOCKS] as $blockId => $data) { + if (!is_string($blockId)) { + continue; + } + $isEmpty = true; + if (!empty($data[ScrollData::SUB_BLOCKS])) { + foreach ($data[ScrollData::SUB_BLOCKS] as $subblockId => $subblockData) { + if (!empty($subblockData[ScrollData::DATA])) { + $isEmpty = false; + } + } + } + + if ($isEmpty) { + $scrollData->removeNamedBlock($blockId); + } + } + } + + /** + * @param ScrollData $scrollData + * @param array $groups + */ + private function addNotEmptyGroupBlocks(ScrollData $scrollData, array $groups) + { + foreach ($groups as $group) { + if (!empty($group['attributes'])) { + /** @var AttributeGroup $currentGroup */ + $currentGroup = $group['group']; + $scrollData->addNamedBlock($currentGroup->getCode(), $currentGroup->getLabel()->getString()); + } + } + } + + /** + * @param BeforeListRenderEvent $event + */ + public function onViewList(BeforeListRenderEvent $event) + { + $entity = $event->getEntity(); + + if (!$entity instanceof AttributeFamilyAwareInterface) { + return; + } + + $groups = $this->attributeManager->getGroupsWithAttributes($entity->getAttributeFamily()); + $scrollData = $event->getScrollData(); + $this->filterGroupAttributes($groups, 'view', 'is_displayable'); + $this->addNotEmptyGroupBlocks($scrollData, $groups); + + /** @var AttributeGroup $group */ + foreach ($groups as $groupData) { + /** @var AttributeGroup $group */ + $group = $groupData['group']; + + /** @var FieldConfigModel $attribute */ + foreach ($groupData['attributes'] as $attribute) { + $fieldName = $attribute->getFieldName(); + if (in_array($fieldName, $this->getRestrictedToMoveFields(), true)) { + continue; + } + if ($scrollData->hasNamedField($fieldName)) { + $this->moveFieldToBlock($scrollData, $fieldName, $group->getCode()); + continue; + } + + $html = $event->getEnvironment()->render( + 'OroEntityConfigBundle:Attribute:attributeView.html.twig', + [ + 'entity' => $entity, + 'field' => $attribute, + ] + ); + + $subblockId = $scrollData->addSubBlock($group->getCode()); + $scrollData->addSubBlockData($group->getCode(), $subblockId, $html, $fieldName); + } + } + + $this->combineGroupBlocks($scrollData); + $this->removeEmptyGroupBlocks($scrollData); + } + + /** + * @param ScrollData $scrollData + * @param string $fieldId + * @param string $blockId + */ + protected function moveFieldToBlock(ScrollData $scrollData, $fieldId, $blockId) + { + if (in_array($fieldId, $this->getRestrictedToMoveFields(), true)) { + return; + } + + $data = $scrollData->getData(); + if (!isset($data[ScrollData::DATA_BLOCKS][$blockId])) { + return; + } + + foreach ($data[ScrollData::DATA_BLOCKS] as $currentBlockId => &$blockData) { + foreach ($blockData[ScrollData::SUB_BLOCKS] as $subblockId => &$subblock) { + if (isset($subblock[ScrollData::DATA][$fieldId])) { + $fieldData = $subblock[ScrollData::DATA][$fieldId]; + + if ($blockId !== $currentBlockId) { + unset($subblock[ScrollData::DATA][$fieldId]); + + $subblockIds = $scrollData->getSubblockIds($blockId); + if (empty($subblockIds)) { + $subblockId = $scrollData->addSubBlock($blockId); + } else { + $subblockId = reset($subblockIds); + } + + $scrollData->addSubBlockData($blockId, $subblockId, $fieldData, $fieldId); + break; + } + } + } + } + } + + /** + * @param array $groups + * @param string $scope + * @param string $option + */ + private function filterGroupAttributes(array &$groups, $scope, $option) + { + foreach ($groups as &$group) { + $group['attributes'] = array_filter( + $group['attributes'], + function (FieldConfigModel $attribute = null) use ($scope, $option) { + if ($attribute) { + $attributeScopedConfig = $attribute->toArray($scope); + return !empty($attributeScopedConfig[$option]); + } + + return false; + } + ); + } + } + + /** + * @return array + */ + protected function getRestrictedToMoveFields() + { + return $this->fieldsRestrictedToMove; + } +} diff --git a/src/Marello/Bundle/ProductBundle/EventListener/Datagrid/ProductGridListener.php b/src/Marello/Bundle/ProductBundle/EventListener/Datagrid/ProductGridListener.php index 3be15ae08..d7f1e3530 100644 --- a/src/Marello/Bundle/ProductBundle/EventListener/Datagrid/ProductGridListener.php +++ b/src/Marello/Bundle/ProductBundle/EventListener/Datagrid/ProductGridListener.php @@ -5,14 +5,74 @@ use Doctrine\ORM\Query; use Doctrine\ORM\Query\Expr\GroupBy; +use Marello\Bundle\ProductBundle\Entity\ProductStatus; use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; use Oro\Bundle\DataGridBundle\Event\BuildAfter; +use Oro\Bundle\DataGridBundle\Event\BuildBefore; use Oro\Bundle\DataGridBundle\Event\OrmResultBefore; use Marello\Bundle\ProductBundle\Datagrid\ORM\Query\ProductsGridSqlWalker; class ProductGridListener { + /** + * @param BuildBefore $event + */ + public function onBuildBefore(BuildBefore $event) + { + $config = $event->getConfig(); + $ormQuery = $config->getOrmQuery(); + $ormQuery + ->addSelect('i.id as image') + ->addSelect('(CASE WHEN p.image IS NOT NULL THEN true ELSE false END) as hasImage') + ->addSelect('s.label as status') + ->addLeftJoin('p.image', 'i') + ->addLeftJoin('p.status', 's') + ->addGroupBy('s.label'); + $columns = $config->offsetGetByPath('[columns]'); + $columns = array_merge( + [ + 'image' => [ + 'label' => 'marello.product.image.label', + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloProductBundle:Product/Datagrid/Property:image.html.twig', + ], + 'status' => [ + 'label' => 'marello.product.status.label', + 'frontend_type' => 'string', + ] + ], + $columns + ); + $config->offsetSetByPath('[columns]', $columns); + $config + ->offsetSetByPath( + '[sorters][columns][status]', + ['data_name' => 's.label'] + ) + ->offsetSetByPath( + '[filters][columns][image]', + [ + 'type' => 'boolean', + 'data_name' => 'hasImage', + ] + ) + ->offsetSetByPath( + '[filters][columns][status]', + [ + 'type' => 'entity', + 'data_name' => 's', + 'options' => [ + 'field_options' => [ + 'multiple' => true, + 'class' => ProductStatus::class + ] + ] + ] + ); + } + /** * @param OrmResultBefore $event */ diff --git a/src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductAttributeFamilyEventListener.php b/src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductAttributeFamilyEventListener.php deleted file mode 100644 index 47a9b07fb..000000000 --- a/src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductAttributeFamilyEventListener.php +++ /dev/null @@ -1,44 +0,0 @@ -productTypesProvider = $productTypesProvider; - } - - /** - * @param LifecycleEventArgs $args - */ - public function prePersist(LifecycleEventArgs $args) - { - $em = $args->getEntityManager(); - $entity = $args->getEntity(); - if ($entity instanceof Product && $entity->getType() && !$entity->getAttributeFamily()) { - $productType = $this->productTypesProvider->getProductType($entity->getType()); - if ($productType && $productType->getAttributeFamilyCode()) { - $attributeFamily = $em - ->getRepository(AttributeFamily::class) - ->findOneBy(['code' => $productType->getAttributeFamilyCode()]); - if ($attributeFamily) { - $entity->setAttributeFamily($attributeFamily); - } - } - } - } -} diff --git a/src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductDropshipEventListener.php b/src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductDropshipEventListener.php index 87b1dd40b..7f0f17a39 100644 --- a/src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductDropshipEventListener.php +++ b/src/Marello/Bundle/ProductBundle/EventListener/Doctrine/ProductDropshipEventListener.php @@ -59,6 +59,17 @@ public function preUpdate(PreUpdateEventArgs $args) } } + /** + * @param LifecycleEventArgs $args + */ + public function preRemove(LifecycleEventArgs $args) + { + $entity = $args->getEntity(); + if ($entity instanceof ProductSupplierRelation && $entity->getCanDropship() === true) { + $this->event= new ProductDropshipEvent($entity, false); + } + } + public function postFlush() { if ($this->event !== null) { diff --git a/src/Marello/Bundle/ProductBundle/Form/EventListener/AttributeFamilySubscriber.php b/src/Marello/Bundle/ProductBundle/Form/EventListener/AttributeFamilySubscriber.php deleted file mode 100644 index 1187e916c..000000000 --- a/src/Marello/Bundle/ProductBundle/Form/EventListener/AttributeFamilySubscriber.php +++ /dev/null @@ -1,90 +0,0 @@ -em = $em; - $this->productTypesProvider = $productTypesProvider; - } - - /** - * Get subscribed events - * @return array - */ - public static function getSubscribedEvents() - { - return [ - FormEvents::POST_SET_DATA => 'postSetData', - FormEvents::PRE_SUBMIT => 'preSubmit' - ]; - } - - /** - * @param FormEvent $event - */ - public function postSetData(FormEvent $event) - { - /** @var Product $product */ - $product = $event->getData(); - $form = $event->getForm(); - $type = $form->get('type')->getData(); - - if (!$product instanceof ProductInterface) { - return; - } - - if (!$product->getAttributeFamily() && $type) { - $productType = $this->productTypesProvider->getProductType($type); - if ($productType) { - $attributeFamily = $this->em - ->getRepository(AttributeFamily::class) - ->findOneBy(['code' => $productType->getAttributeFamilyCode()]); - $product->setAttributeFamily($attributeFamily); - } - } - - $event->setData($product); - } - - public function preSubmit(FormEvent $event) - { - $data = $event->getData(); - if ($data['type']) { - $productType = $this->productTypesProvider->getProductType($data['type']); - if ($productType) { - $attributeFamily = $this->em - ->getRepository(AttributeFamily::class) - ->findOneBy(['code' => $productType->getAttributeFamilyCode()]); - $data['attributeFamily'] = $attributeFamily; - } - - $event->setData($data); - } - } -} diff --git a/src/Marello/Bundle/ProductBundle/Form/Handler/ProductHandler.php b/src/Marello/Bundle/ProductBundle/Form/Handler/ProductHandler.php index 75386e678..e184f16a9 100644 --- a/src/Marello/Bundle/ProductBundle/Form/Handler/ProductHandler.php +++ b/src/Marello/Bundle/ProductBundle/Form/Handler/ProductHandler.php @@ -8,7 +8,6 @@ use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\ProductBundle\Entity\ProductChannelTaxRelation; use Marello\Bundle\ProductBundle\Entity\ProductSupplierRelation; -use Marello\Bundle\ProductBundle\Form\Type\ProductType; use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Marello\Bundle\SupplierBundle\Entity\Supplier; use Oro\Bundle\FormBundle\Form\Handler\RequestHandlerTrait; diff --git a/src/Marello/Bundle/ProductBundle/Form/Handler/ProductsSalesChannelsAssignHandler.php b/src/Marello/Bundle/ProductBundle/Form/Handler/ProductsSalesChannelsAssignHandler.php index 5b7eccfec..8ff7d9874 100644 --- a/src/Marello/Bundle/ProductBundle/Form/Handler/ProductsSalesChannelsAssignHandler.php +++ b/src/Marello/Bundle/ProductBundle/Form/Handler/ProductsSalesChannelsAssignHandler.php @@ -3,24 +3,32 @@ namespace Marello\Bundle\ProductBundle\Form\Handler; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\QueryBuilder; -use Marello\Bundle\ProductBundle\Async\ProductsAssignSalesChannelsProcessor; -use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\SalesBundle\Entity\SalesChannel; + +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +use Oro\Component\MessageQueue\Util\JSON; use Oro\Bundle\DataGridBundle\Datagrid\Manager; use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; use Oro\Bundle\FormBundle\Form\Handler\RequestHandlerTrait; use Oro\Component\MessageQueue\Client\MessageProducerInterface; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; + +use Marello\Bundle\ProductBundle\Entity\Product; +use Marello\Bundle\SalesBundle\Entity\SalesChannel; +use Marello\Bundle\ProductBundle\Async\ProductsAssignSalesChannelsProcessor; class ProductsSalesChannelsAssignHandler { use RequestHandlerTrait; + /** @var int size that will determine wether the products should be saved immediately or send to the queue */ const FLUSH_BATCH_SIZE = 100; + /** @var int max size of product ids per message to prevent having a single big message */ + const MESSAGE_PRODUCT_ID_SIZE = 1000; + /** * @var FormInterface */ @@ -68,15 +76,20 @@ public function __construct( } /** - * @return bool + * @return array * @throws \Exception */ - public function process() + public function process(): array { + $filtersFromRequest = null; + if (null !== $this->request->query->get('filters')) { + $filtersFromRequest = JSON::encode($this->request->query->get('filters')); + } + $this->form->setData([ 'inset' => $this->request->query->get('inset'), 'products' => $this->request->query->get('values'), - 'filters' => json_encode($this->request->query->get('filters')), + 'filters' => $filtersFromRequest ]); if (in_array($this->request->getMethod(), ['POST', 'PUT'])) { @@ -85,9 +98,11 @@ public function process() if ($this->form->isValid()) { $inset = $this->form->get('inset')->getData(); $products = $this->form->get('products')->getData(); - $filters = json_decode($this->form->get('filters')->getData(), true); $addChannels = $this->form->get('addSalesChannels')->getData(); - + $filters = null; + if (null !== $this->form->get('filters')->getData()) { + JSON::decode($this->form->get('filters')->getData()); + } return $this->onSuccess( $addChannels, $inset, @@ -101,13 +116,14 @@ public function process() } /** - * @param array $addChannels - * @param string $inset - * @param string|null $products - * @param array|null $filters + * @param $addChannels + * @param $inset + * @param null $products + * @param null $filters * @return array + * @throws \Oro\Component\MessageQueue\Transport\Exception\Exception */ - private function onSuccess($addChannels, $inset, $products = null, $filters = null) + private function onSuccess($addChannels, $inset, $products = null, $filters = null): array { $isAllSelected = $this->isAllSelected($inset); $productIds = explode(',', $products); @@ -127,32 +143,28 @@ private function onSuccess($addChannels, $inset, $products = null, $filters = nu $queryBuilder->andWhere($queryBuilder->expr()->notIn('p.id', $productIds)); } - $countQueryBuilder = clone $queryBuilder; - - $result = $countQueryBuilder - ->resetDQLParts(['select', 'groupBy']) - ->select(['COUNT(DISTINCT p.id) AS count']) + $queryResult = $queryBuilder ->getQuery() ->setFirstResult(0) ->setMaxResults(null) - ->getOneOrNullResult(); - if ((int)$result['count'] <= self::FLUSH_BATCH_SIZE) { - $this->processSmallData($queryBuilder, $addChannels); + ->getArrayResult(); + + if ((int)count($queryResult) <= self::FLUSH_BATCH_SIZE) { + $this->processSmallData($queryResult, $addChannels); return [ 'success' => true, 'type' => 'success', 'message' => 'marello.product.messages.success.sales_channels.assignment' ]; - } else { - $this->processBigData($inset, $productIds, $filters, $addChannels); - - return [ - 'success' => true, - 'type' => 'info', - 'message' => 'marello.product.messages.success.sales_channels.assignment_started' - ]; } + + $this->processBigData($queryResult, $addChannels); + return [ + 'success' => true, + 'type' => 'info', + 'message' => 'marello.product.messages.success.sales_channels.assignment_started' + ]; } return ['success' => false, 'message' => 'marello.product.messages.error.sales_channels.assignment']; @@ -162,36 +174,33 @@ private function onSuccess($addChannels, $inset, $products = null, $filters = nu * @param string $inset * @return bool */ - protected function isAllSelected($inset) + protected function isAllSelected($inset): bool { return $inset === '0'; } /** - * @param QueryBuilder $queryBuilder + * @param array $queryResult * @param SalesChannel[] $salesChannels */ - private function processSmallData($queryBuilder, array $salesChannels) + private function processSmallData(array $queryResult, array $salesChannels): void { - $result = $queryBuilder - ->getQuery() - ->setFirstResult(0) - ->setMaxResults(null) - ->getResult(); + $productIds = $this->getProductIdsFromResult($queryResult); + $products = $this->entityManager + ->getRepository(Product::class) + ->findBy(['id' => $productIds]); + $iteration = 1; - foreach ($result as $entity) { - /** @var Product $entity */ - $entity = $entity[0]; + /** @var Product $product */ + foreach ($products as $product) { $addedChannels = 0; foreach ($salesChannels as $salesChannel) { - if (!$entity->hasChannel($salesChannel)) { - $entity->addChannel($salesChannel); + if (!$product->hasChannel($salesChannel)) { + $product->addChannel($salesChannel); + $this->entityManager->persist($product); $addedChannels++; } } - if ($addedChannels > 0) { - $this->entityManager->persist($entity); - } if (($iteration % self::FLUSH_BATCH_SIZE) === 0) { $this->entityManager->flush(); @@ -200,27 +209,46 @@ private function processSmallData($queryBuilder, array $salesChannels) $iteration++; } } + $this->entityManager->flush(); } /** - * @param int $inset - * @param array|null $products - * @param array|null $filters - * @param SalesChannel[]|null $salesChannels + * @param array $queryResult + * @param SalesChannel[] $salesChannels + * @throws \Oro\Component\MessageQueue\Transport\Exception\Exception */ - private function processBigData($inset, array $products = null, array $filters = null, array $salesChannels = null) + private function processBigData(array $queryResult, array $salesChannels): void { - $channelIds = []; - foreach ($salesChannels as $salesChannel) { - $channelIds[] = $salesChannel->getCode(); + $channelIds = array_map( + static function (SalesChannel $channel) { + return $channel->getId(); + }, + $salesChannels + ); + + $productIds = $this->getProductIdsFromResult($queryResult); + if (count($productIds) > self::MESSAGE_PRODUCT_ID_SIZE) { + $chunks = array_chunk($productIds, self::MESSAGE_PRODUCT_ID_SIZE); + foreach ($chunks as $chunk) { + $this->sendProductsToMessageQueue($chunk, $channelIds); + } + } else { + $this->sendProductsToMessageQueue($productIds, $channelIds); } + } + + /** + * @param array $productIds + * @param array $channelIds + * @throws \Oro\Component\MessageQueue\Transport\Exception\Exception + */ + private function sendProductsToMessageQueue(array $productIds, array $channelIds) + { $this->messageProducer->send( ProductsAssignSalesChannelsProcessor::TOPIC, [ - 'inset' => $inset, - 'products' => $products, - 'filters' => $filters, + 'products' => $productIds, 'salesChannels' => $channelIds, 'jobId' => md5(rand(1, 5)) ] @@ -228,12 +256,29 @@ private function processBigData($inset, array $products = null, array $filters = } /** - * Returns form instance + * Returns form view instance * - * @return FormInterface + * @return FormView */ - public function getFormView() + public function getFormView(): FormView { return $this->form->createView(); } + + /** + * @param array $result + * @return array + */ + private function getProductIdsFromResult(array $result): array + { + return array_map( + static function ($entityAsArray) { + if (array_key_exists('id', $entityAsArray)) { + return $entityAsArray['id']; + } + return null; + }, + $result + ); + } } diff --git a/src/Marello/Bundle/ProductBundle/Form/Type/ProductChannelTaxRelationCollectionType.php b/src/Marello/Bundle/ProductBundle/Form/Type/ProductChannelTaxRelationCollectionType.php index 302fc3ab3..cf2f485e6 100644 --- a/src/Marello/Bundle/ProductBundle/Form/Type/ProductChannelTaxRelationCollectionType.php +++ b/src/Marello/Bundle/ProductBundle/Form/Type/ProductChannelTaxRelationCollectionType.php @@ -23,7 +23,7 @@ public function configureOptions(OptionsResolver $resolver) 'constraints' => [new Valid()], 'prototype_name' => '__nameproductchanneltaxrelation__', 'prototype' => true, - 'handle_primary' => false, + 'handle_primary' => true, ]); } diff --git a/src/Marello/Bundle/ProductBundle/Form/Type/ProductSupplierRelationType.php b/src/Marello/Bundle/ProductBundle/Form/Type/ProductSupplierRelationType.php index 4b8d5f6bf..db2115183 100644 --- a/src/Marello/Bundle/ProductBundle/Form/Type/ProductSupplierRelationType.php +++ b/src/Marello/Bundle/ProductBundle/Form/Type/ProductSupplierRelationType.php @@ -3,9 +3,13 @@ namespace Marello\Bundle\ProductBundle\Form\Type; use Marello\Bundle\ProductBundle\Entity\ProductSupplierRelation; +use Marello\Bundle\SupplierBundle\Entity\Supplier; use Marello\Bundle\SupplierBundle\Form\Type\SupplierSelectType; +use Oro\Bundle\CurrencyBundle\Model\LocaleSettings; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Valid; @@ -13,6 +17,19 @@ class ProductSupplierRelationType extends AbstractType { const BLOCK_PREFIX = 'marello_product_supplier_relation_form'; + /** + * @var LocaleSettings + */ + protected $localeSettings; + + /** + * @param LocaleSettings $localeSettings + */ + public function __construct(LocaleSettings $localeSettings) + { + $this->localeSettings = $localeSettings; + } + /** * {@inheritdoc} */ @@ -46,6 +63,16 @@ public function configureOptions(OptionsResolver $resolver) ]); } + public function finishView(FormView $view, FormInterface $form, array $options) + { + $data = $form->get('supplier')->getData(); + if ($data instanceof Supplier) { + $view->vars['currency'] = $this->localeSettings->getCurrencySymbolByCurrency($data->getCurrency()); + } else { + $view->vars['currency'] = null; + } + } + /** * {@inheritdoc} */ diff --git a/src/Marello/Bundle/ProductBundle/Form/Type/ProductType.php b/src/Marello/Bundle/ProductBundle/Form/Type/ProductType.php index 6d195aa46..03fa864ea 100644 --- a/src/Marello/Bundle/ProductBundle/Form/Type/ProductType.php +++ b/src/Marello/Bundle/ProductBundle/Form/Type/ProductType.php @@ -6,11 +6,12 @@ use Marello\Bundle\PricingBundle\Form\EventListener\ChannelPricingSubscriber; use Marello\Bundle\PricingBundle\Form\EventListener\PricingSubscriber; use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\ProductBundle\Form\EventListener\AttributeFamilySubscriber; use Marello\Bundle\SalesBundle\Form\EventListener\DefaultSalesChannelSubscriber; use Marello\Bundle\TaxBundle\Form\Type\TaxCodeSelectType; use Oro\Bundle\AttachmentBundle\Form\Type\ImageType; +use Oro\Bundle\FormBundle\Form\Extension\StripTagsExtension; use Oro\Bundle\FormBundle\Form\Type\EntityIdentifierType; +use Oro\Bundle\LocaleBundle\Form\Type\LocalizedFallbackValueCollectionType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\AbstractType; @@ -19,6 +20,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Valid; class ProductType extends AbstractType @@ -40,32 +42,28 @@ class ProductType extends AbstractType */ protected $channelPricingSubscriber; - /** - * @var AttributeFamilySubscriber - */ - protected $attributeFamilySubscriber; - /** * @var EventSubscriberInterface */ protected $subscriptionProductSubscriber; /** + * ProductType constructor. * @param DefaultSalesChannelSubscriber $defaultSalesChannelSubscriber * @param PricingSubscriber $pricingSubscriber * @param ChannelPricingSubscriber $channelPricingSubscriber - * @param AttributeFamilySubscriber $attributeFamilySubscriber + * @param EventSubscriberInterface|null $subscriptionProductSubscriber */ public function __construct( DefaultSalesChannelSubscriber $defaultSalesChannelSubscriber, PricingSubscriber $pricingSubscriber, ChannelPricingSubscriber $channelPricingSubscriber, - AttributeFamilySubscriber $attributeFamilySubscriber + EventSubscriberInterface $subscriptionProductSubscriber = null ) { $this->defaultSalesChannelSubscriber = $defaultSalesChannelSubscriber; $this->pricingSubscriber = $pricingSubscriber; $this->channelPricingSubscriber = $channelPricingSubscriber; - $this->attributeFamilySubscriber = $attributeFamilySubscriber; + $this->subscriptionProductSubscriber = $subscriptionProductSubscriber; } /** @@ -75,13 +73,16 @@ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('type', HiddenType::class) - ->add('attributeFamily', HiddenType::class) ->add( - 'name', - TextType::class, + 'names', + LocalizedFallbackValueCollectionType::class, [ + 'label' => 'marello.product.names.label', 'required' => true, - 'label' => 'marello.product.name.label', + 'entry_options' => [ + 'constraints' => [new NotBlank(['message' => 'marello.product.messages.error.names.blank'])], + StripTagsExtension::OPTION_NAME => true, + ], ] ) ->add( @@ -194,7 +195,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->addEventSubscriber($this->defaultSalesChannelSubscriber); $builder->addEventSubscriber($this->pricingSubscriber); $builder->addEventSubscriber($this->channelPricingSubscriber); - $builder->addEventSubscriber($this->attributeFamilySubscriber); if ($this->subscriptionProductSubscriber) { $builder->addEventSubscriber($this->subscriptionProductSubscriber); } @@ -210,6 +210,7 @@ public function configureOptions(OptionsResolver $resolver) 'intention' => 'product', 'single_form' => true, 'enable_attributes' => true, + 'enable_attribute_family' => true, 'constraints' => [new Valid()], ]); } @@ -221,14 +222,4 @@ public function getBlockPrefix() { return self::BLOCK_PREFIX; } - - /** - * Added because of keeping BC - * @deprecated will be removed in 3.0 - * @param EventSubscriberInterface|null $subscriptionProductSubscriber - */ - public function setEventSubscriberInterface(EventSubscriberInterface $subscriptionProductSubscriber = null) - { - $this->subscriptionProductSubscriber = $subscriptionProductSubscriber; - } } diff --git a/src/Marello/Bundle/ProductBundle/MarelloProductBundle.php b/src/Marello/Bundle/ProductBundle/MarelloProductBundle.php index 904026624..9ef1989da 100644 --- a/src/Marello/Bundle/ProductBundle/MarelloProductBundle.php +++ b/src/Marello/Bundle/ProductBundle/MarelloProductBundle.php @@ -3,6 +3,8 @@ namespace Marello\Bundle\ProductBundle; use Marello\Bundle\ProductBundle\DependencyInjection\Compiler\ProductTypesPass; +use Marello\Bundle\ProductBundle\Entity\Product; +use Oro\Bundle\LocaleBundle\DependencyInjection\Compiler\DefaultFallbackExtensionPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -13,8 +15,14 @@ class MarelloProductBundle extends Bundle */ public function build(ContainerBuilder $container) { - $container->addCompilerPass(new ProductTypesPass()); - parent::build($container); + + $container + ->addCompilerPass(new ProductTypesPass()) + ->addCompilerPass(new DefaultFallbackExtensionPass([ + Product::class => [ + 'name' => 'names' + ] + ])); } } diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/AssignAttributesToDefaultFamily.php b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/AssignAttributesToDefaultFamily.php new file mode 100644 index 000000000..74b9a76ec --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/AssignAttributesToDefaultFamily.php @@ -0,0 +1,117 @@ + [ + 'searchable' => true, + 'filterable' => true, + 'filter_by' => 'exact_value', + 'sortable' => true, + ], + 'names' => [ + 'searchable' => true, + 'filterable' => true, + 'filter_by' => 'exact_value', + 'sortable' => true, + ], + 'channels' => [ + 'visible' => true + ], + 'status' => [ + 'visible' => true + ], + 'prices' => [ + 'visible' => true + ], + 'channelPrices' => [ + 'visible' => true + ], + 'taxCode' => [ + 'visible' => true + ], + 'salesChannelTaxCodes' => [ + 'visible' => true + ], + 'weight' => [ + 'visible' => true + ], + 'manufacturingCode' => [ + 'visible' => true + ], + 'warranty' => [ + 'visible' => true + ], + 'suppliers' => [ + 'visible' => true + ], + 'categories' => [ + 'visible' => true + ], + 'image' => [ + 'visible' => true + ], + ]; + + /** + * {@inheritdoc} + */ + public function load(ObjectManager $manager) + { + $this->updateProductAttributes(self::ATTRIBUTES); + $this->addGroup($manager); + } + + /** + * @param ObjectManager $manager + */ + private function addGroup(ObjectManager $manager) + { + $attributeFamilyRepository = $manager->getRepository(AttributeFamily::class); + + $defaultFamily = + $attributeFamilyRepository->findOneBy([ + 'code' => LoadDefaultAttributeFamilyData::DEFAULT_FAMILY_CODE + ]); + + $attributeGroup = $defaultFamily->getAttributeGroup(LoadDefaultAttributeFamilyData::GENERAL_GROUP_CODE); + + $configManager = $this->getConfigManager(); + foreach (self::ATTRIBUTES as $attribute => $data) { + $fieldConfigModel = $configManager->getConfigFieldModel(Product::class, $attribute); + $attributeGroupRelation = new AttributeGroupRelation(); + $attributeGroupRelation->setEntityConfigFieldId($fieldConfigModel->getId()); + $attributeGroup->addAttributeRelation($attributeGroupRelation); + } + + $manager->persist($attributeGroup); + $manager->flush(); + } + + /** + * @inheritdoc + */ + public function getDependencies() + { + return [ + LoadDefaultAttributeFamilyData::class + ]; + } +} diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/LoadDefaultAttributeFamilyData.php b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/LoadDefaultAttributeFamilyData.php index c7412a979..7b45dfebf 100644 --- a/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/LoadDefaultAttributeFamilyData.php +++ b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/LoadDefaultAttributeFamilyData.php @@ -5,20 +5,19 @@ use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; - -use Oro\Bundle\UserBundle\Entity\User; -use Oro\Bundle\OrganizationBundle\Entity\Organization; -use Oro\Bundle\UserBundle\Migrations\Data\ORM\LoadAdminUserData; -use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeGroup; +use Marello\Bundle\ProductBundle\Entity\Product; use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily; +use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeGroup; +use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue; +use Oro\Bundle\OrganizationBundle\Entity\Organization; use Oro\Bundle\OrganizationBundle\Migrations\Data\ORM\LoadOrganizationAndBusinessUnitData; - -use Marello\Bundle\ProductBundle\Entity\Product; +use Oro\Bundle\UserBundle\Migrations\Data\ORM\LoadAdminUserData; class LoadDefaultAttributeFamilyData extends AbstractFixture implements DependentFixtureInterface { const DEFAULT_FAMILY_CODE = 'marello_default'; const GENERAL_GROUP_CODE = 'general'; + const GENERAL_GROUP_LABEL = 'General'; /** @var ObjectManager $manager */ protected $manager; @@ -28,7 +27,7 @@ class LoadDefaultAttributeFamilyData extends AbstractFixture implements Dependen */ protected $data = [ [ - 'groupLabel' => 'General', + 'groupLabel' => self::GENERAL_GROUP_LABEL, 'groupCode' => self::GENERAL_GROUP_CODE, 'attributes' => [], 'groupVisibility' => false @@ -50,16 +49,12 @@ public function getDependencies() */ public function load(ObjectManager $manager) { - /** @var User $user */ - $user = $manager - ->getRepository(User::class)->findOneBy(['username' => 'admin']); $organization = $this->getOrganization($manager); $attributeFamily = new AttributeFamily(); $attributeFamily->setCode(self::DEFAULT_FAMILY_CODE); $attributeFamily->setEntityClass(Product::class); - $attributeFamily->setDefaultLabel('Default'); - $attributeFamily->setOrganization($organization); - $attributeFamily->setOwner($user); + $attributeFamily->addLabel((new LocalizedFallbackValue())->setString('Default')); + $attributeFamily->setOwner($organization); $this->setReference(self::DEFAULT_FAMILY_CODE, $attributeFamily); @@ -95,7 +90,7 @@ protected function addGroupsWithAttributesToFamily( ) { foreach ($groupsData as $groupData) { $attributeGroup = new AttributeGroup(); - $attributeGroup->setDefaultLabel($groupData['groupLabel']); + $attributeGroup->addLabel((new LocalizedFallbackValue())->setString($groupData['groupLabel'])); $attributeGroup->setIsVisible($groupData['groupVisibility']); $attributeGroup->setCode($groupData['groupCode']); $attributeFamily->addAttributeGroup($attributeGroup); diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/MakeProductAttributesTrait.php b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/MakeProductAttributesTrait.php index fbd1e033c..e242a5599 100644 --- a/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/MakeProductAttributesTrait.php +++ b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/MakeProductAttributesTrait.php @@ -19,7 +19,7 @@ trait MakeProductAttributesTrait * @param array $fields * @param string $owner */ - private function makeProductAttributes(array $fields, $owner = ExtendScope::OWNER_CUSTOM) + private function makeProductAttributes(array $fields, $owner = ExtendScope::ORIGIN_SYSTEM) { $configManager = $this->getConfigManager(); $configHelper = $this->container->get('oro_entity_config.config.config_helper'); diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/UpdateExistingProductsWithLocalizedName.php b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/UpdateExistingProductsWithLocalizedName.php new file mode 100644 index 000000000..f02872d12 --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/UpdateExistingProductsWithLocalizedName.php @@ -0,0 +1,46 @@ +getRepository(Product::class) + ->createQueryBuilder('p') + ->leftJoin('p.names', 'n') + ->having('COUNT(n.id) = 0') + ->groupBy('p.id') + ->getQuery() + ->getResult(); + + if (count($products) === 0) { + return; + } + + foreach ($products as $product) { + $product->setDefaultName($product->getDenormalizedDefaultName()); + $manager->persist($product); + } + + $manager->flush(); + } +} diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Schema/MarelloProductBundleInstaller.php b/src/Marello/Bundle/ProductBundle/Migrations/Schema/MarelloProductBundleInstaller.php index 8a5a36551..11e693e07 100644 --- a/src/Marello/Bundle/ProductBundle/Migrations/Schema/MarelloProductBundleInstaller.php +++ b/src/Marello/Bundle/ProductBundle/Migrations/Schema/MarelloProductBundleInstaller.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Schema\Schema; +use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope; use Oro\Bundle\MigrationBundle\Migration\QueryBag; use Oro\Bundle\MigrationBundle\Migration\Installation; use Oro\Bundle\AttachmentBundle\Migration\Extension\AttachmentExtensionAwareTrait; @@ -28,7 +29,7 @@ class MarelloProductBundleInstaller implements */ public function getMigrationVersion() { - return 'v1_7'; + return 'v1_9'; } /** @@ -38,6 +39,7 @@ public function up(Schema $schema, QueryBag $queries) { /** Tables generation **/ $this->createMarelloProductProductTable($schema); + $this->createMarelloProductProductNameTable($schema); $this->createMarelloProductProductStatusTable($schema); $this->createMarelloProductSaleschannelTable($schema); $this->createMarelloProductVariantTable($schema); @@ -46,6 +48,7 @@ public function up(Schema $schema, QueryBag $queries) /** Foreign keys generation **/ $this->addMarelloProductProductForeignKeys($schema); + $this->addMarelloProductNameForeignKeys($schema); $this->addMarelloProductSaleschannelForeignKeys($schema); $this->addMarelloProductSalesChannelTaxRelationForeignKeys($schema); $this->addMarelloProductSupplierRelationForeignKeys($schema); @@ -72,19 +75,9 @@ protected function createMarelloProductProductTable(Schema $schema) $table->addColumn('name', 'string', ['length' => 255]); $table->addColumn('sku', 'string', ['length' => 255]); $table->addColumn('manufacturing_code', 'string', ['length' => 255, 'notnull' => false]); - $table->addColumn( - 'price', - 'money', - ['notnull' => false, 'precision' => 19, 'scale' => 4, 'comment' => '(DC2Type:money)'] - ); $table->addColumn('created_at', 'datetime'); - $table->addColumn('updated_at', 'datetime'); + $table->addColumn('updated_at', 'datetime', ['notnull' => false]); $table->addColumn('type', 'string', ['notnull' => false, 'length' => 255]); - $table->addColumn( - 'cost', - 'money', - ['notnull' => false, 'precision' => 19, 'scale' => 4, 'comment' => '(DC2Type:money)'] - ); $table->addColumn('data', 'json_array', ['notnull' => false, 'comment' => '(DC2Type:json_array)']); $table->addColumn('weight', 'float', ['notnull' => false]); $table->addColumn('warranty', 'integer', ['notnull' => false]); @@ -99,6 +92,18 @@ protected function createMarelloProductProductTable(Schema $schema) $table->addIndex(['tax_code_id']); } + /** + * @param Schema $schema + */ + protected function createMarelloProductProductNameTable(Schema $schema) + { + $table = $schema->createTable('marello_product_product_name'); + $table->addColumn('product_id', 'integer', []); + $table->addColumn('localized_value_id', 'integer', []); + $table->setPrimaryKey(['product_id', 'localized_value_id']); + $table->addUniqueIndex(['localized_value_id'], 'uniq_ba57d521eb576e89'); + } + /** * Create marello_product_product_status table * @@ -233,6 +238,26 @@ protected function addMarelloProductProductForeignKeys(Schema $schema) ); } + /** + * @param Schema $schema + */ + protected function addMarelloProductNameForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_product_product_name'); + $table->addForeignKeyConstraint( + $schema->getTable('oro_fallback_localization_val'), + ['localized_value_id'], + ['id'], + ['onUpdate' => null, 'onDelete' => 'CASCADE'] + ); + $table->addForeignKeyConstraint( + $schema->getTable('marello_product_product'), + ['product_id'], + ['id'], + ['onUpdate' => null, 'onDelete' => 'CASCADE'] + ); + } + /** * Add marello_product_saleschannel foreign keys. * @@ -317,6 +342,8 @@ protected function addImageRelation(Schema $schema) 'image', [ 'importexport' => ['excluded' => true], + 'attribute' => ['is_attribute' => true], + 'extend' => ['owner' => ExtendScope::OWNER_CUSTOM], 'attachment' => [ 'acl_protected' => false ] diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_3/MarelloProductBundle.php b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_3/MarelloProductBundle.php index a305cdf0b..1f925382f 100644 --- a/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_3/MarelloProductBundle.php +++ b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_3/MarelloProductBundle.php @@ -3,9 +3,6 @@ namespace Marello\Bundle\ProductBundle\Migrations\Schema\v1_3; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope; -use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtension; -use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtensionAwareInterface; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_8/RemovePriceAndCostColumns.php b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_8/RemovePriceAndCostColumns.php new file mode 100644 index 000000000..7c368cd8a --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_8/RemovePriceAndCostColumns.php @@ -0,0 +1,26 @@ +getTable(MarelloProductBundleInstaller::PRODUCT_TABLE); + if ($table->hasColumn('price')) { + $table->dropColumn('price'); + } + if ($table->hasColumn('cost')) { + $table->dropColumn('cost'); + } + $table->changeColumn('updated_at', ['notnull' => false]); + } +} diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/AddLocalizedNamesToProduct.php b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/AddLocalizedNamesToProduct.php new file mode 100644 index 000000000..a1f2502e3 --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/AddLocalizedNamesToProduct.php @@ -0,0 +1,70 @@ +createMarelloProductProductNameTable($schema); + $this->addMarelloProductNameForeignKeys($schema); + + $dropFields = ['name', 'cost']; + $dropFieldInConfigSql = <<addSql( + $dropFieldInConfigSql, + ['field_name' => $field, 'class_name' => Product::class], + ['field_name' => Type::STRING, 'class_name' => Type::STRING] + ); + $queries->addPostQuery($dropFieldInConfigQuery); + } + } + + /** + * @param Schema $schema + */ + protected function createMarelloProductProductNameTable(Schema $schema) + { + $table = $schema->createTable('marello_product_product_name'); + $table->addColumn('product_id', 'integer', []); + $table->addColumn('localized_value_id', 'integer', []); + $table->setPrimaryKey(['product_id', 'localized_value_id']); + $table->addUniqueIndex(['localized_value_id'], 'uniq_ba57d521eb576e89'); + } + + /** + * @param Schema $schema + */ + protected function addMarelloProductNameForeignKeys(Schema $schema) + { + $table = $schema->getTable('marello_product_product_name'); + $table->addForeignKeyConstraint( + $schema->getTable('oro_fallback_localization_val'), + ['localized_value_id'], + ['id'], + ['onUpdate' => null, 'onDelete' => 'CASCADE'] + ); + $table->addForeignKeyConstraint( + $schema->getTable('marello_product_product'), + ['product_id'], + ['id'], + ['onUpdate' => null, 'onDelete' => 'CASCADE'] + ); + } +} diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/UpdateAttachmentFieldConfigForProductImage.php b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/UpdateAttachmentFieldConfigForProductImage.php new file mode 100644 index 000000000..9e7551101 --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Migrations/Schema/v1_9/UpdateAttachmentFieldConfigForProductImage.php @@ -0,0 +1,28 @@ +addPostQuery( + new UpdateEntityConfigFieldValueQuery(Product::class, 'image', 'attribute', 'is_attribute', true) + ); + $queries->addPostQuery( + new UpdateEntityConfigFieldValueQuery(Product::class, 'image', 'extend', 'owner', ExtendScope::OWNER_CUSTOM) + ); + } +} diff --git a/src/Marello/Bundle/ProductBundle/Model/ExtendProduct.php b/src/Marello/Bundle/ProductBundle/Model/ExtendProduct.php index f21c31d32..99461d6e9 100644 --- a/src/Marello/Bundle/ProductBundle/Model/ExtendProduct.php +++ b/src/Marello/Bundle/ProductBundle/Model/ExtendProduct.php @@ -2,28 +2,17 @@ namespace Marello\Bundle\ProductBundle\Model; -use Oro\Bundle\BusinessEntitiesBundle\Entity\BaseProduct; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; +use Oro\Bundle\LocaleBundle\Entity\Localization; +use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue; /** - * Class ExtendProduct - * @package Marello\Bundle\ProductBundle\Model - * + * @method LocalizedFallbackValue getName(Localization $localization = null) + * @method LocalizedFallbackValue getDefaultName() + * @method setDefaultName(string $value) */ -class ExtendProduct extends BaseProduct +class ExtendProduct { - /** - * @Oro\ConfigField( - * defaultValues={ - * "importexport"={ - * "excluded"=true - * } - * } - * ) - */ - protected $price; - - public function __construct() { } diff --git a/src/Marello/Bundle/ProductBundle/Model/ProductType.php b/src/Marello/Bundle/ProductBundle/Model/ProductType.php index 9c0844519..5b2ddbc94 100644 --- a/src/Marello/Bundle/ProductBundle/Model/ProductType.php +++ b/src/Marello/Bundle/ProductBundle/Model/ProductType.php @@ -8,7 +8,6 @@ class ProductType extends ParameterBag implements ProductTypeInterface { const NAME_FIELD = 'name'; const LABEL_FIELD = 'label'; - const ATTRIBUTE_FAMILY_CODE = 'attribute_family_code'; /** * @inheritDoc @@ -25,12 +24,4 @@ public function getLabel() { return $this->get(self::LABEL_FIELD); } - - /** - * @inheritDoc - */ - public function getAttributeFamilyCode() - { - return $this->get(self::ATTRIBUTE_FAMILY_CODE); - } } diff --git a/src/Marello/Bundle/ProductBundle/Model/ProductTypeInterface.php b/src/Marello/Bundle/ProductBundle/Model/ProductTypeInterface.php index 51b7bc882..391bd5bc3 100644 --- a/src/Marello/Bundle/ProductBundle/Model/ProductTypeInterface.php +++ b/src/Marello/Bundle/ProductBundle/Model/ProductTypeInterface.php @@ -13,9 +13,4 @@ public function getName(); * @return string */ public function getLabel(); - - /** - * @return string - */ - public function getAttributeFamilyCode(); } diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/form.yml b/src/Marello/Bundle/ProductBundle/Resources/config/form.yml index 952a18f61..15572ded7 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/config/form.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/form.yml @@ -1,73 +1,54 @@ -parameters: - ## form types - marello_product.product_form.type.product_step_one.class: Marello\Bundle\ProductBundle\Form\Type\ProductStepOneType - marello_product.product_form.type.class: Marello\Bundle\ProductBundle\Form\Type\ProductType - marello_product.product_form.type.product_select.class: Marello\Bundle\ProductBundle\Form\Type\ProductSelectType - marello_product.product_form.type.product_type_select.class: Marello\Bundle\ProductBundle\Form\Type\ProductTypeSelectType - marello_product.product_form.type.product_select_collection.class: Marello\Bundle\ProductBundle\Form\Type\ProductSelectCollectionType - marello_product.product_form.type.product_sales_channel_aware_select.class: Marello\Bundle\ProductBundle\Form\Type\ProductSalesChannelAwareSelectType - marello_product.form.type.product_collection.class: Marello\Bundle\ProductBundle\Form\Type\ProductCollectionType - marello_product.product_variant_form.type.class: Marello\Bundle\ProductBundle\Form\Type\ProductVariantType - marello_product.form.type.product_supplier_select.class: Marello\Bundle\ProductBundle\Form\Type\ProductSupplierSelectType - marello_product.form.type.products_assign_sales_channels.class: Marello\Bundle\ProductBundle\Form\Type\ProductsAssignSalesChannelsType - - ## form handlers - marello_product.product_form.handler.class: Marello\Bundle\ProductBundle\Form\Handler\ProductHandler - marello_product.product_variant_form.handler.class: Marello\Bundle\ProductBundle\Form\Handler\ProductVariantHandler - services: ## form types marello_product.product_form.type.product_step_one: - class: '%marello_product.product_form.type.product_step_one.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductStepOneType' tags: - { name: form.type } marello_product.product_form.type: - class: '%marello_product.product_form.type.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductType' arguments: - '@marello_sales.form.event_listener.default_sales_channel_subscriber' - '@marello_productprice.pricing.form.event_listener.pricing_subscriber' - '@marello_productprice.pricing.form.event_listener.channel_pricing_subscriber' - - '@marello_product.product.form.event_listener.attribute_family' - calls: - - ['setEventSubscriberInterface', ['@?marello_subscription.form.event_listener.subscription_product']] + - '@?marello_subscription.form.event_listener.subscription_product' tags: - { name: form.type } marello_product.product_form.type.product_select: - class: '%marello_product.product_form.type.product_select.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductSelectType' tags: - { name: form.type } marello_product.product_form.type.product_select_collection: - class: '%marello_product.product_form.type.product_select_collection.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductSelectCollectionType' tags: - { name: form.type } marello_product.product_form.type.product_type_select: - class: '%marello_product.product_form.type.product_type_select.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductTypeSelectType' arguments: - '@marello_product.provider.product_types' tags: - { name: form.type } marello_product.product_form.type.product_sales_channel_aware_select: - class: '%marello_product.product_form.type.product_sales_channel_aware_select.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductSalesChannelAwareSelectType' tags: - { name: form.type } marello_product.form.type.product_supplier_select: - class: '%marello_product.form.type.product_supplier_select.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductSupplierSelectType' tags: - { name: form.type } marello_product.form.type.product_collection: - class: '%marello_product.form.type.product_collection.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductCollectionType' tags: - { name: form.type } marello_product.product_variant_form.type: - class: '%marello_product.product_variant_form.type.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductVariantType' tags: - { name: form.type } @@ -83,6 +64,8 @@ services: marello_product.form.type.product_supplier_relation: class: Marello\Bundle\ProductBundle\Form\Type\ProductSupplierRelationType + arguments: + - '@oro_currency.locale_settings' tags: - { name: form.type } @@ -92,7 +75,7 @@ services: - { name: form.type } marello_product.form.type.products_assign_sales_channels: - class: '%marello_product.form.type.products_assign_sales_channels.class%' + class: 'Marello\Bundle\ProductBundle\Form\Type\ProductsAssignSalesChannelsType' tags: - { name: form.type } ## forms @@ -123,16 +106,16 @@ services: ## form handlers marello_product.product_form.handler: - class: '%marello_product.product_form.handler.class%' - scope: request + class: 'Marello\Bundle\ProductBundle\Form\Handler\ProductHandler' + public: true arguments: - '@marello_product.product.form' - '@request_stack' - '@doctrine.orm.entity_manager' marello_product.product_variant_form.handler: - class: '%marello_product.product_variant_form.handler.class%' - scope: request + class: 'Marello\Bundle\ProductBundle\Form\Handler\ProductVariantHandler' + public: true arguments: - '@marello_product.product_variant.form' - '@request_stack' @@ -141,6 +124,7 @@ services: marello_product.sales_channels_assign.handler: class: 'Marello\Bundle\ProductBundle\Form\Handler\ProductsSalesChannelsAssignHandler' + public: true arguments: - '@marello_product.products_assign_sales_channels.form' - '@request_stack' diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/ProductBundle/Resources/config/jsmodules.yml new file mode 100755 index 000000000..cfee855c2 --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Resources/config/jsmodules.yml @@ -0,0 +1,11 @@ +aliases: + oro/select2-autocomplete-sales-channel-aware-component$: marelloproduct/js/app/components/select2-autocomplete-sales-channel-aware-component +dynamic-imports: + marelloproduct: + - marelloproduct/js/app/components/select-create-inline-type-component + - marelloproduct/js/app/components/select-create-inline-type-async-component + - marelloproduct/js/app/components/select2-autocomplete-sales-channel-aware-component + - marelloproduct/js/app/views/product-channel-taxcode-view + - marelloproduct/js/app/views/product-channel-taxcodes-view + commons: + - oro/select2-autocomplete-sales-channel-aware-component \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/oro/actions.yml b/src/Marello/Bundle/ProductBundle/Resources/config/oro/actions.yml index 77d0a2017..0dd0ca1bb 100755 --- a/src/Marello/Bundle/ProductBundle/Resources/config/oro/actions.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/oro/actions.yml @@ -18,8 +18,12 @@ operations: parameters_mapping: data: $.data - '@call_service_method': + attribute: $.flashBag + service: session + method: getFlashBag + - '@call_method': attribute: $.hasSuccessMessage - service: session.flash_bag + object: $.flashBag method: has method_parameters: ['success'] - '@flash_message': diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/oro/api.yml b/src/Marello/Bundle/ProductBundle/Resources/config/oro/api.yml index 36ac2c98e..6a966356c 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/config/oro/api.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/oro/api.yml @@ -19,8 +19,6 @@ api: property_path: taxCode productstatus: property_path: status - price: - exclude: true createdAt: exclude: true updatedAt: diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/ProductBundle/Resources/config/oro/datagrids.yml index e69fcd692..ab9a04267 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/oro/datagrids.yml @@ -9,45 +9,14 @@ datagrids: - p.id - p.sku - p.type - - IDENTITY(p.image) as image - - p.name - p.weight - - p.name - p.manufacturingCode - p.createdAt - p.updatedAt - - tc.code as taxCode - - (CASE WHEN p.image IS NOT NULL THEN true ELSE false END) as hasImage - - s.label as status - - count(sc.code) as channelsCount - - count(cat.id) as categoriesCount - - sum(defpr.value) as defaultPricesSum - - sum(sppr.value) as specialPricesSum - - sum(mspr.value) as msrpPricesSum - - sum(defcpr.value) as defaultChannelPricesSum - - sum(spcpr.value) as specialChannelPricesSum from: - { table: MarelloProductBundle:Product, alias: p } - join: - left: - - { join: p.status, alias: s } - - { join: p.taxCode, alias: tc } - - { join: p.channels, alias: sc } - - { join: p.categories, alias: cat } - - { join: p.prices, alias: pr } - - { join: pr.defaultPrice, alias: defpr } - - { join: pr.specialPrice, alias: sppr } - - { join: pr.msrpPrice, alias: mspr } - - { join: p.channelPrices, alias: cpr } - - { join: cpr.defaultPrice, alias: defcpr } - - { join: cpr.specialPrice, alias: spcpr } - groupBy: p.id, s.label + groupBy: p.id columns: - image: - label: marello.product.image.label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:image.html.twig type: label: marello.product.type.label frontend_type: string @@ -55,14 +24,7 @@ datagrids: label: marello.product.sku.label frontend_type: string name: - label: marello.product.name.label - frontend_type: string - status: - label: marello.product.status.label - frontend_type: string - taxCode: - label: marello.product.tax_code.label - frontend_type: string + label: marello.product.names.label manufacturingCode: label: marello.product.manufacturing_code.label frontend_type: string @@ -71,53 +33,6 @@ datagrids: label: marello.product.weight.label frontend_type: number renderable: false - defaultPrices: - label: marello.pricing.assembledpricelist.default_price.plural_label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:defaultPrices.html.twig - renderable: false - align: right - specialPrices: - label: marello.pricing.assembledpricelist.special_price.plural_label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:specialPrices.html.twig - renderable: false - align: right - msrpPrices: - label: marello.pricing.assembledpricelist.msrp_price.plural_label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:msrpPrices.html.twig - renderable: false - align: right - defaultChannelPrices: - label: marello.pricing.assembledchannelpricelist.default_price.plural_label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:defaultChannelPrices.html.twig - renderable: false - align: right - specialChannelPrices: - label: marello.pricing.assembledchannelpricelist.special_price.plural_label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:specialChannelPrices.html.twig - renderable: false - align: right - channels: - label: marello.product.channels.label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:channels.html.twig - renderable: false - categories: - label: marello.product.categories.label - type: twig - frontend_type: html - template: MarelloProductBundle:Product/Datagrid/Property:categories.html.twig - renderable: false createdAt: label: oro.ui.created_at frontend_type: datetime @@ -127,6 +42,9 @@ datagrids: renderable: false properties: id: ~ + name: + type: localized_value + data_name: names view_link: type: url route: marello_product_view @@ -139,26 +57,14 @@ datagrids: columns: type: { data_name: p.type } sku: { data_name: p.sku } - name: { data_name: p.name } - status: { data_name: s.label } + name: { data_name: name } weight: { data_name: p.weight } - taxCode: { data_name: p.taxCode } - channels: { data_name: channelsCount } - categories: { data_name: categoriesCount } - defaultPrices: { data_name: defaultPricesSum } - specialPrices: { data_name: specialPricesSum } - defaultChannelPrices: { data_name: defaultChannelPricesSum } - specialChannelPrices: { data_name: specialChannelPricesSum } - msrpPrices: { data_name: msrpPricesSum } createdAt: { data_name: p.createdAt } updatedAt: { data_name: p.updatedAt } default: - name: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + name: 'DESC' filters: columns: - image: - type: boolean - data_name: hasImage type: type: string data_name: p.type @@ -167,65 +73,16 @@ datagrids: data_name: p.sku name: type: string - data_name: p.name - status: - type: entity - data_name: s - options: - field_options: - class: Marello\Bundle\ProductBundle\Entity\ProductStatus + data_name: name manufacturingCode: data_name: p.manufacturingCode type: string enabled: false - defaultPrices: - data_name: defpr.value - type: number - enabled: false - specialPrices: - data_name: sppr.value - type: number - enabled: false - msrpPrices: - data_name: mspr.value - type: number - enabled: false - defaultChannelPrices: - data_name: defcpr.value - type: number - enabled: false - specialChannelPrices: - data_name: spcpr.value - type: number - enabled: false - taxCode: - type: choice - data_name: tc.id - options: - field_options: - multiple: true - choices: '@marello_tax.provider.tax_codes_choices->getTaxCodes()' - channels: - type: choice - data_name: sc.code - options: - field_options: - multiple: true - choices: '@marello_sales.provider.basic_sales_channels_choices->getChannels()' - enabled: false - categories: - type: choice - data_name: cat.code - options: - field_options: - multiple: true - choices: '@marello_catalog.provider.categories_choices->getCategories()' - enabled: false weight: data_name: p.weight type: number options: - data_type: '%oro_filter.form.type.filter.number.class%::DATA_DECIMAL' + data_type: 'Oro\Bundle\FilterBundle\Form\Type\Filter\NumberFilterType::DATA_DECIMAL' enabled: false actions: view: @@ -271,6 +128,7 @@ datagrids: - channelId mass_actions: ~ options: + entityHint: marello.product.entity_plural_label export: false entity_pagination: false @@ -285,7 +143,6 @@ datagrids: acl_resource: marello_product_view query: select: - - p.name - p.sku - s.label as status - COALESCE(SUM(il.inventory), 0) AS inventoryQty @@ -309,8 +166,7 @@ datagrids: label: marello.product.sku.label frontend_type: string name: - label: marello.product.name.label - frontend_type: string + label: marello.product.names.label inventoryQty: label: marello.inventory.inventorylevel.inventory.label frontend_type: number @@ -318,13 +174,17 @@ datagrids: status: label: marello.product.status.label frontend_type: string + properties: + name: + type: localized_value + data_name: names sorters: columns: - name: { data_name: p.name } + name: { data_name: name } sku: { data_name: p.sku } inventoryQty: { data_name: inventoryQty } default: - name: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC" + name: "DESC" options: entityHint: product toolbarOptions: @@ -347,12 +207,11 @@ datagrids: ELSE false END) as hasVariant - - p.name - p.sku - s.label as status - COALESCE(SUM(il.inventory), 0) AS inventoryQty from: - - { table: Marello\Bundle\ProductBundle\Entity\Product, alias: p } + - { table: MarelloProductBundle:Product, alias: p } join: left: - { join: p.status, alias: s } @@ -380,8 +239,7 @@ datagrids: label: marello.product.sku.label frontend_type: string name: - label: marello.product.name.label - frontend_type: string + label: marello.product.names.label inventoryQty: label: marello.inventory.inventorylevel.inventory.label frontend_type: number @@ -396,22 +254,25 @@ datagrids: type: boolean data_name: hasVariant name: - label: marello.product.name.label + label: marello.product.names.label type: string - data_name: p.name + data_name: name sku: label: marello.product.sku.label type: string data_name: p.sku sorters: columns: - name: { data_name: p.name } + name: { data_name: name } sku: { data_name: p.sku } hasVariant: { data_name: hasVariant } default: - hasVariant: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC" + hasVariant: "DESC" properties: id: ~ + name: + type: localized_value + data_name: names options: entityHint: product rowSelection: @@ -526,7 +387,7 @@ datagrids: sorters: columns: hasChannel: { data_name: hasChannel } - default: { hasChannel: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' } + default: { hasChannel: 'DESC' } filters: columns: hasChannel: @@ -604,7 +465,7 @@ datagrids: select: - p.id - p.sku - - p.name + - p.denormalizedDefaultName as productName - p.manufacturingCode from: - { table: MarelloProductBundle:Product, alias: p } @@ -612,9 +473,10 @@ datagrids: sku: label: marello.product.sku.label frontend_type: string - name: + productName: label: marello.product.name.label frontend_type: string + data_name: productName manufacturingCode: label: marello.product.manufacturing_code.label frontend_type: string @@ -624,10 +486,10 @@ datagrids: label: marello.product.sku.label type: string data_name: p.sku - name: + productName: label: marello.product.name.label type: string - data_name: p.name + data_name: productName manufacturingCode: label: marello.product.manufacturing_code.label type: string @@ -635,10 +497,10 @@ datagrids: sorters: columns: sku: { data_name: p.sku } - name: { data_name: p.name } + productName: { data_name: productName } manufacturingCode: { data_name: p.manufacturingCode } default: - name: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC' + name: 'ASC' properties: id: ~ options: @@ -646,7 +508,7 @@ datagrids: marello-product-categories-base-grid: acl_resource: marello_category_update - extended_entity_name: '%marello_catalog.category.entity.class%' + extended_entity_name: Marello\Bundle\CatalogBundle\Entity\Category source: type: orm query: @@ -657,7 +519,7 @@ datagrids: - category.createdAt - category.updatedAt from: - - { table: '%marello_catalog.category.entity.class%', alias: category } + - { table: MarelloCatalogBundle:Category, alias: category } columns: code: label: marello.catalog.category.code.label @@ -710,7 +572,7 @@ datagrids: - categories sorters: default: - name: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + name: 'DESC' marello-product-categories-extend-grid: extends: marello-product-categories-base-grid @@ -732,7 +594,7 @@ datagrids: join: left: - - join: '%marello_product.entity.class%' + join: Marello\Bundle\ProductBundle\Entity\Product alias: categoryProduct conditionType: WITH condition: 'categoryProduct = :product_id' @@ -751,7 +613,7 @@ datagrids: inCategory: data_name: inCategory default: - inCategory: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + inCategory: 'DESC' filters: columns: inCategory: diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/oro/placeholders.yml b/src/Marello/Bundle/ProductBundle/Resources/config/oro/placeholders.yml index 3c250683e..9a31d596b 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/config/oro/placeholders.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/oro/placeholders.yml @@ -2,9 +2,9 @@ placeholders: items: marello_add_variant_link: template: MarelloProductBundle:Variant:addVariantLink.html.twig - applicable: '@oro_ui.placeholder.filter->isInstanceOf($entity$, %marello_product.entity.class%)' + applicable: '@oro_ui.placeholder.filter->isInstanceOf($entity$, Marello\Bundle\ProductBundle\Entity\Product)' acl: marello_product_create_variant marello_add_variant_button: template: MarelloProductBundle:Variant:addVariantButton.html.twig - applicable: '@oro_ui.placeholder.filter->isInstanceOf($entity$, %marello_product.entity.class%)' + applicable: '@oro_ui.placeholder.filter->isInstanceOf($entity$, Marello\Bundle\ProductBundle\Entity\Product)' acl: marello_product_create_variant \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/oro/search.yml b/src/Marello/Bundle/ProductBundle/Resources/config/oro/search.yml index 5c4e72ef8..ea79dc6c8 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/config/oro/search.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/oro/search.yml @@ -2,7 +2,7 @@ search: Marello\Bundle\ProductBundle\Entity\Product: alias: marello_product label: marello.product.entity_plural_label - title_fields: [name] + title_fields: [denormalizedDefaultName] route: name: marello_product_view parameters: @@ -10,9 +10,17 @@ search: search_template: MarelloProductBundle:Product:searchResult.html.twig fields: - - name: name + name: denormalizedDefaultName target_type: text - target_fields: [name] + target_fields: [denormalizedDefaultName] + - + name: names + relation_type: many-to-many + relation_fields: + - + name: string + target_type: text + target_fields: [names] - name: sku target_type: text diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/requirejs.yml b/src/Marello/Bundle/ProductBundle/Resources/config/requirejs.yml deleted file mode 100755 index a05d39024..000000000 --- a/src/Marello/Bundle/ProductBundle/Resources/config/requirejs.yml +++ /dev/null @@ -1,5 +0,0 @@ -config: - paths: - 'marelloproduct/js/app/components/select-create-inline-type-component': 'bundles/marelloproduct/js/app/components/select-create-inline-type-component.js' - 'marelloproduct/js/app/components/select-create-inline-type-async-component': 'bundles/marelloproduct/js/app/components/select-create-inline-type-async-component.js' - 'oro/select2-autocomplete-sales-channel-aware-component': 'bundles/marelloproduct/js/app/components/select2-autocomplete-sales-channel-aware-component.js' diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/services.yml b/src/Marello/Bundle/ProductBundle/Resources/config/services.yml index 46d0385c0..8a3810381 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/services.yml @@ -1,13 +1,10 @@ -parameters: - marello_product.entity.class: Marello\Bundle\ProductBundle\Entity\Product - services: # Autocomplete search handler marello_product.form.autocomplete.product.search_handler: parent: oro_form.autocomplete.search_handler arguments: - - '%marello_product.entity.class%' - - ["name", "sku"] + - 'Marello\Bundle\ProductBundle\Entity\Product' + - ["denormalizedDefaultName", "sku"] tags: - { name: oro_form.autocomplete.search_handler, alias: products, acl_resource: marello_product_view } @@ -15,8 +12,8 @@ services: class: 'Marello\Bundle\ProductBundle\Autocomplete\SalesChannelProductHandler' parent: oro_form.autocomplete.search_handler arguments: - - '%marello_product.entity.class%' - - ["name", "sku"] + - 'Marello\Bundle\ProductBundle\Entity\Product' + - ["denormalizedDefaultName", "sku"] tags: - { name: oro_form.autocomplete.search_handler, alias: sales_channel_products, acl_resource: marello_product_view } @@ -28,6 +25,13 @@ services: tags: - { name: oro_ui.view_action_provider, group: product } + marello_product.twig.dynamic_fields_extension: + class: Marello\Bundle\ProductBundle\Twig\DynamicFieldsExtension + arguments: + - '@oro_entity_config.manager.attribute_manager' + tags: + - { name: twig.extension } + marello_product.twig.product_extension: class: Marello\Bundle\ProductBundle\Twig\ProductExtension arguments: @@ -62,16 +66,10 @@ services: marello_product.listener.datagrid.product_grid_listener: class: Marello\Bundle\ProductBundle\EventListener\Datagrid\ProductGridListener tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-products-grid, method: onBuildBefore } - { name: kernel.event_listener, event: oro_datagrid.orm_datasource.result.before.marello-products-grid, method: onResultBefore } - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.after.marello-products-grid, method: buildAfter } - marello_product.listener.doctrine.product_attribute_family: - class: Marello\Bundle\ProductBundle\EventListener\Doctrine\ProductAttributeFamilyEventListener - arguments: - - '@marello_product.provider.product_types' - tags: - - { name: doctrine.event_listener, event: prePersist } - marello_product.listener.doctrine.product_dropship: class: Marello\Bundle\ProductBundle\EventListener\Doctrine\ProductDropshipEventListener arguments: @@ -79,6 +77,7 @@ services: tags: - { name: doctrine.event_listener, event: prePersist } - { name: doctrine.event_listener, event: preUpdate } + - { name: doctrine.event_listener, event: preRemove } - { name: doctrine.event_listener, event: postFlush } marello_product.product_search_listener: @@ -106,6 +105,7 @@ services: marello_product.service.duplicator: class: 'Marello\Bundle\ProductBundle\Duplicator\ProductDuplicator' + public: true arguments: - "@oro_entity.doctrine_helper" - "@event_dispatcher" @@ -119,7 +119,7 @@ services: public: false arguments: - "@oro_entity.doctrine_helper" - - '%marello_product.entity.class%' + - 'Marello\Bundle\ProductBundle\Entity\Product' marello_product.virtual_fields.decorator_factory: class: 'Marello\Bundle\ProductBundle\VirtualFields\VirtualFieldsProductDecoratorFactory' @@ -143,6 +143,7 @@ services: class: 'Marello\Bundle\ProductBundle\Datagrid\Extension\MassAction\SalesChannelsAssignMassAction' arguments: - 'marello_product_assign_sales_channels' + public: true shared: false tags: - { name: oro_datagrid.extension.mass_action.type, type: sales_channels_assign } @@ -159,26 +160,23 @@ services: tags: - { name: 'oro_message_queue.client.message_processor' } + marello_product.event_listener.attribute_family_form_view: parent: oro_entity_config.event_listener.attribute_family_form_view.abstract tags: - - { name: kernel.event_listener, event: oro_ui.scroll_data.before.marello-product-create-step-two, method: onEdit, priority: -255 } - - { name: kernel.event_listener, event: oro_ui.scroll_data.before.marello-product-update, method: onEdit } + - { name: kernel.event_listener, event: oro_ui.scroll_data.before.marello-product-create-step-one, method: onEdit } marello_product.event_listener.attributes_form_view: - parent: oro_entity_config.event_listener.attributes_form_view.abstract + class: Marello\Bundle\ProductBundle\EventListener\AttributeFormViewListener + arguments: + - '@oro_entity_config.manager.attribute_manager' tags: - { name: kernel.event_listener, event: oro_ui.scroll_data.before.marello-product-create-step-two, method: onEdit, priority: -255 } - { name: kernel.event_listener, event: oro_ui.scroll_data.before.marello-product-update, method: onEdit, priority: -255 } - { name: kernel.event_listener, event: oro_ui.scroll_data.before.marello-product-view, method: onViewList, priority: -255 } - marello_product.product.form.event_listener.attribute_family: - class: Marello\Bundle\ProductBundle\Form\EventListener\AttributeFamilySubscriber - arguments: - - '@doctrine.orm.entity_manager' - - '@marello_product.provider.product_types' - marello_product.provider.product_types: + public: true class: Marello\Bundle\ProductBundle\Provider\ProductTypesProvider marello_product.product_type.simple: @@ -186,6 +184,12 @@ services: arguments: - name: 'simple' label: 'Simple' - attribute_family_code: 'marello_default' tags: - - {name: marello_product.product_type} \ No newline at end of file + - {name: marello_product.product_type} + + marello_product.form.product_supplier_relations_dropship_validator: + class: 'Marello\Bundle\ProductBundle\Validator\ProductSupplierRelationsDropshipValidator' + arguments: + - '@doctrine' + tags: + - { name: validator.constraint_validator, alias: marello_product.product_supplier_relations_dropship_validator } diff --git a/src/Marello/Bundle/ProductBundle/Resources/config/validation.yml b/src/Marello/Bundle/ProductBundle/Resources/config/validation.yml index ff3665e42..31ad2a288 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/config/validation.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/config/validation.yml @@ -1,13 +1,12 @@ Marello\Bundle\ProductBundle\Entity\Product: constraints: + - Marello\Bundle\ProductBundle\Validator\Constraints\ProductSupplierRelationsDropship: ~ - Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity: fields: sku message: 'marello.product.messages.error.sku' properties: - name: - - NotBlank: ~ - - Length: - max: 255 + names: + - Oro\Bundle\PlatformBundle\Validator\Constraints\ValidLoadedItems: ~ sku: - NotBlank: ~ prices: diff --git a/src/Marello/Bundle/ProductBundle/Resources/doc/api/product.md b/src/Marello/Bundle/ProductBundle/Resources/doc/api/product.md index 2219c21ee..0bbdd4351 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/doc/api/product.md +++ b/src/Marello/Bundle/ProductBundle/Resources/doc/api/product.md @@ -32,15 +32,20 @@ Example: "id": "100909", "attributes": { "productId": 848, - "name": "9'4\" Super Magnum", + "denormalizedDefaultName": "9'4\" Super Magnum", "manufacturingCode": "TZH-529-udz-090", "productType": null, - "cost": null, "weight": 12, "warranty": null, "data": [] }, "relationships": { + "names": { + "data": { + "type": "localizedfallbackvalues", + "id": 1 + } + }, "status": { "data": { "type": "productstatuses", diff --git a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/channel-aware-select-create-component-mixin.js b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/channel-aware-select-create-component-mixin.js index 9e068492b..49159a3a7 100755 --- a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/channel-aware-select-create-component-mixin.js +++ b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/channel-aware-select-create-component-mixin.js @@ -1,11 +1,11 @@ define(function(require) { 'use strict'; - var selectCreateComponentMixin, + const $ = require('jquery'), mediator = require('oroui/js/mediator'); - selectCreateComponentMixin = { + const selectCreateComponentMixin = { options: { salesChannelDataContainer: '.marello-sales-channel-select-container', @@ -21,6 +21,8 @@ define(function(require) { var channelData = $(this.options.salesChannelDataContainer).data(this.options.attribute); if (channelData !== undefined) { this.saveData(channelData.id); + } else { + this.saveData(0); } mediator.on('marello_sales:channel:changed', this.onSalesChannelChange, this); }, diff --git a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-async-component.js b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-async-component.js index 4939587d8..ed514c5c1 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-async-component.js +++ b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-async-component.js @@ -1,11 +1,10 @@ define(function(require) { 'use strict'; - var SelectCreateInlineTypeAsyncComponent; - var _ = require('underscore'); - var ParentComponent = require('oroform/js/app/components/select-create-inline-type-async-component'); - var mixin = require('./channel-aware-select-create-component-mixin'); - SelectCreateInlineTypeAsyncComponent = ParentComponent.extend(_.extend({}, mixin, { + const _ = require('underscore'); + const ParentComponent = require('oroform/js/app/components/select-create-inline-type-async-component'); + const mixin = require('./channel-aware-select-create-component-mixin'); + const SelectCreateInlineTypeAsyncComponent = ParentComponent.extend(_.extend({}, mixin, { _super: function() { return SelectCreateInlineTypeAsyncComponent.__super__; } diff --git a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-component.js b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-component.js index 6df1958e3..c1aed6ffe 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-component.js +++ b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select-create-inline-type-component.js @@ -1,11 +1,10 @@ define(function(require) { 'use strict'; - var SelectCreateInlineTypeComponent; - var _ = require('underscore'); - var ParentComponent = require('oroform/js/app/components/select-create-inline-type-component'); - var mixin = require('./channel-aware-select-create-component-mixin'); - SelectCreateInlineTypeComponent = ParentComponent.extend(_.extend({}, mixin, { + const _ = require('underscore'); + const ParentComponent = require('oroform/js/app/components/select-create-inline-type-component'); + const mixin = require('./channel-aware-select-create-component-mixin'); + const SelectCreateInlineTypeComponent = ParentComponent.extend(_.extend({}, mixin, { _super: function() { return SelectCreateInlineTypeComponent.__super__; } diff --git a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select2-autocomplete-sales-channel-aware-component.js b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select2-autocomplete-sales-channel-aware-component.js index 0db1b6dbd..ec6ebe40a 100755 --- a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select2-autocomplete-sales-channel-aware-component.js +++ b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/components/select2-autocomplete-sales-channel-aware-component.js @@ -1,9 +1,9 @@ define(function (require) { 'use strict'; - var Select2AutocompleteChannelAwareComponent, + const mediator = require('oroui/js/mediator'), Select2AutocompleteComponent = require('oro/select2-autocomplete-component'); - Select2AutocompleteChannelAwareComponent = Select2AutocompleteComponent.extend({ + const Select2AutocompleteChannelAwareComponent = Select2AutocompleteComponent.extend({ /** * @property {Object} diff --git a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcode-view.js b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcode-view.js index c1c7f6385..3f52b5ead 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcode-view.js +++ b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcode-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var ProductChannelTaxView, + const $ = require('jquery'), _ = require('underscore'), mediator = require('oroui/js/mediator'), @@ -12,7 +12,7 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemView * @class marelloproduct.app.views.ProductChannelTaxView */ - ProductChannelTaxView = AbstractItemView.extend({ + const ProductChannelTaxView = AbstractItemView.extend({ options: { priority: 0, canDropship: false diff --git a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcodes-view.js b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcodes-view.js index 65c70afcd..e1f48a240 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcodes-view.js +++ b/src/Marello/Bundle/ProductBundle/Resources/public/js/app/views/product-channel-taxcodes-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var ProductChannelTaxCodesView, + const $ = require('jquery'), _ = require('underscore'), routing = require('routing'), @@ -13,7 +13,7 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemsView * @class marelloproduct.app.views.ProductChannelTaxCodesView */ - ProductChannelTaxCodesView = AbstractItemsView.extend({ + const ProductChannelTaxCodesView = AbstractItemsView.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/ProductBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/ProductBundle/Resources/translations/messages.en.yml index 97ef709e7..5313e61e7 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/translations/messages.en.yml @@ -4,12 +4,11 @@ marello: entity_plural_label: Products entity_label: Product name.label: Name + names.label: Names sku.label: SKU weight.label: Weight (in Kg) warranty.label: Product Warranty - price.label: Default price status.label: Status - cost.label: Cost data.label: Data type.label: Product Type preferred_supplier.label: Preferred Supplier @@ -72,7 +71,7 @@ marello: assignment: There was failure during Sales Channels assignment sections: - general: General Information + general: General additional: Additional form.choose_product: Select Product diff --git a/src/Marello/Bundle/ProductBundle/Resources/translations/validators.en.yml b/src/Marello/Bundle/ProductBundle/Resources/translations/validators.en.yml index 5d07c1d61..eb5b44137 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/translations/validators.en.yml +++ b/src/Marello/Bundle/ProductBundle/Resources/translations/validators.en.yml @@ -2,8 +2,10 @@ marello: product: messages: error: + names.blank: Product Name should not be blank. sku: SKU already exists, please provide a unique SKU channels: Please select a Sales Channel. There should be at least one Sales Channel per product. + suppliers.dropship: It is forbidden to remove the ProductSupplierRelation with dropship enabled or disable dropship if it has inventory in external warehouse. Please allocate or remove inventory before deletion productchanneltaxrelation: messages: diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Form/fields.html.twig index cf8d3dd78..f78b4276c 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Form/fields.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Form/fields.html.twig @@ -13,7 +13,7 @@ {% endblock %} -{% macro marello_product_channel_tax_relation_collection_form_item_prototype(widget) %} +{% macro marello_product_channel_tax_relation_collection_form_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -32,7 +32,7 @@ {% set page_component_options = { 'disabled': not allow_delete } %} - {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_product_channel_tax_relation_collection_form_item_prototype(child) }} + {{ fields.marello_product_channel_tax_relation_collection_form_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -97,7 +100,7 @@ {% block oro_entity_create_or_select_inline_js_sales_channel_aware %} {% import 'OroUIBundle::macros.html.twig' as UI %} - {% if form.vars.configs.async_dialogs|default(false) is sameas(true) %} + {% if form.vars.configs.async_dialogs|default(false) is same as(true) %} {% set asyncNameSegment = 'async-' %} {% endif %} {% set urlParts = { @@ -111,7 +114,7 @@ } } %} - {% if form.vars.create_enabled|default(false) is sameas(true) %} + {% if form.vars.create_enabled|default(false) is same as(true) %} {% set urlParts = urlParts|merge({ create: { route: form.vars.create_form_route, @@ -147,7 +150,7 @@
- {{ form_widget(form.cost) }} + {{ form_widget(form.cost) }}{{ form.vars.currency }}
{{ form_errors(form.cost) }} @@ -165,7 +168,7 @@ {% endblock %} -{% macro marello_product_supplier_relation_collection_form_item_prototype(widget) %} +{% macro marello_product_supplier_relation_collection_form_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -184,7 +187,7 @@ {% set page_component_options = { 'disabled': not allow_delete } %} - {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_product_supplier_relation_collection_form_item_prototype(child) }} + {{ fields.marello_product_supplier_relation_collection_form_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -284,9 +290,12 @@ {% endmacro %} {% block marello_product_select_collection_form_widget %} + {% import _self as fields %} + {% spaceless %} + {% set widgetContainerAttributes = block('widget_container_attributes') %} {% if prototype is defined %} - {% set prototype_html = _self.marello_product_select_collection_form_item_prototype(form) %} + {% set prototype_html = fields.marello_product_select_collection_form_item_prototype(form) %} {% endif %} {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} {% set id = id ~ '_collection' %} @@ -305,7 +314,7 @@ {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_product_select_collection_form_item_prototype(child) }} + {{ fields.marello_product_select_collection_form_item_prototype(child) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/result.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/result.html.twig index c2625e5dc..262875e96 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/result.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/result.html.twig @@ -1 +1 @@ -<%= highlight(_.escape(sku)) %> - <%= highlight(_.escape(name)) %> +<%= highlight(_.escape(sku)) %> - <%= highlight(_.escape(denormalizedDefaultName)) %> diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/selection.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/selection.html.twig index c2625e5dc..262875e96 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/selection.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Autocomplete/selection.html.twig @@ -1 +1 @@ -<%= highlight(_.escape(sku)) %> - <%= highlight(_.escape(name)) %> +<%= highlight(_.escape(sku)) %> - <%= highlight(_.escape(denormalizedDefaultName)) %> diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultChannelPrices.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultChannelPrices.html.twig deleted file mode 100644 index 3d72c7762..000000000 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultChannelPrices.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% for index, assembledChannelPriceList in record.getValue('channelPrices') %} - {% if assembledChannelPriceList.defaultPrice %} - {{ assembledChannelPriceList.channel.name }}: {{ assembledChannelPriceList.defaultPrice.value|oro_format_currency({'currency': assembledChannelPriceList.currency}) }}
- {% endif %} -{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultPrices.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultPrices.html.twig deleted file mode 100644 index d6c0d8f6d..000000000 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/defaultPrices.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% for index, assembledPriceList in record.getValue('prices') %} - {% if assembledPriceList.defaultPrice %} - {{ assembledPriceList.defaultPrice.value|oro_format_currency({'currency': assembledPriceList.currency}) }}
- {% endif %} -{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/msrpPrices.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/msrpPrices.html.twig deleted file mode 100644 index 91a84cf7f..000000000 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/msrpPrices.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% for index, assembledPriceList in record.getValue('prices') %} - {% if assembledPriceList.msrpPrice %} - {{ assembledPriceList.msrpPrice.value|oro_format_currency({'currency': assembledPriceList.currency}) }}
- {% endif %} -{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialChannelPrices.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialChannelPrices.html.twig deleted file mode 100644 index dba1a9095..000000000 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialChannelPrices.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% for index, assembledChannelPriceList in record.getValue('channelPrices') %} - {% if assembledChannelPriceList.specialPrice %} - {{ assembledChannelPriceList.channel.name }}: {{ assembledChannelPriceList.specialPrice.value|oro_format_currency({'currency': assembledChannelPriceList.currency}) }}
- {% endif %} -{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialPrices.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialPrices.html.twig deleted file mode 100644 index be2724117..000000000 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/specialPrices.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% for index, assembledPriceList in record.getValue('prices') %} - {% if assembledPriceList.specialPrice %} - {{ assembledPriceList.specialPrice.value|oro_format_currency({'currency': assembledPriceList.currency}) }}
- {% endif %} -{% endfor %} \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/assignSalesChannels.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/assignSalesChannels.html.twig index f10bfb82d..d2305ee8b 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/assignSalesChannels.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/assignSalesChannels.html.twig @@ -1,6 +1,7 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% set gridName = 'marello-products-assign-saleschannels-grid' %} {% set formAttr = formAttr|default({})|merge({ 'data-collect': 'true' diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepOne.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepOne.html.twig index 55faf1e72..6b59d21b8 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepOne.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepOne.html.twig @@ -1,5 +1,6 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% oro_title_set({params : {} }) %} diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepTwo.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepTwo.html.twig index c51af809a..22dc56023 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepTwo.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/createStepTwo.html.twig @@ -1,10 +1,8 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} -{% form_theme form - 'MarelloPricingBundle:Form:fields.html.twig' - 'MarelloSupplierBundle:Form:fields.html.twig' - 'MarelloProductBundle:Form:fields.html.twig' -%} + {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} +{% import 'OroUIBundle::macros.html.twig' as UI %} + {% set gridName = 'marello-product-saleschannel-extended-grid' %} {% set entity = form.vars.value %} @@ -107,36 +105,47 @@ {% endset %} - {% set suppliers %} -
- {{ form_widget(form.suppliers) }} - {{ form_errors(form.suppliers) }} -
- {% endset %} + {% if marello_entity_has_attribute(entity, 'suppliers') %} + {% set suppliers %} +
+ {{ form_widget(form.suppliers) }} + {{ form_errors(form.suppliers) }} +
+ {% endset %} + {% endif %} {% set channelIds = marello_sales_get_saleschannel_ids(entity)|length > 0 ? marello_sales_get_saleschannel_ids(entity) : 0 %} + {% set generalData = [ + form_row(form.names), + form_row(form.sku), + form_row(form.status), + ] %} + {% if marello_entity_has_attribute(entity, 'weight') %} + {% set generalData = generalData|merge([form_row(form.weight)]) %} + {% endif %} + {% if marello_entity_has_attribute(entity, 'warranty') %} + {% set generalData = generalData|merge([form_row(form.warranty)]) %} + {% endif %} + {% if marello_entity_has_attribute(entity, 'manufacturingCode') %} + {% set generalData = generalData|merge([form_row(form.manufacturingCode)]) %} + {% endif %} + {% set generalData = generalData|merge([form_row(form.type)]) %} + {% if marello_entity_has_attribute(entity, 'image') %} + {% set generalData = generalData|merge([form_row(form.image)]) %} + {% endif %} + {% set dataBlocks = [ { 'title': 'marello.product.sections.general'|trans, 'subblocks': [{ 'title': 'General', - 'data': [ - form_row(form.name), - form_row(form.sku), - form_row(form.status), - form_row(form.weight), - form_row(form.warranty), - form_row(form.manufacturingCode), - form_row(form.type), - form_row(form.attributeFamily), - form_row(form.image), - ] + 'data': generalData }] }, { @@ -172,30 +181,38 @@ dataGrid.renderGrid(gridName, { product: form.vars.value.id ? : null, channels: channelIds }) ] }] - }, - { - 'title': 'marello.supplier.entity_plural_label'|trans, - 'class': 'active', - 'subblocks': [{ - 'title': '', - 'data': [ - suppliers - ] - }] - }, - { - 'title' : 'marello.catalog.category.entity_plural_label'|trans, - 'subblocks': [{ - 'title' : null, - 'useSpan': false, - 'data' : [ - form_widget(form.appendCategories, {'id': 'productAppendCategories'}), - form_widget(form.removeCategories, {'id': 'productRemoveCategories'}), - dataGrid.renderGrid('marello-product-categories-extend-grid', { product_id: entity.id ? entity.id : 0 }, { cssClass: 'inner-grid' }) - ] - }] } ] %} + {% if marello_entity_has_attribute(entity, 'suppliers') %} + {% set dataBlocks = dataBlocks|merge([ + { + 'title': 'marello.supplier.entity_plural_label'|trans, + 'class': 'active', + 'subblocks': [{ + 'title': '', + 'data': { + 'suppliers': suppliers + } + }] + } + ]) %} + {% endif %} + {% if marello_entity_has_attribute(entity, 'categories') %} + {% set dataBlocks = dataBlocks|merge([ + { + 'title' : 'marello.catalog.category.entity_plural_label'|trans, + 'subblocks': [{ + 'title' : null, + 'useSpan': false, + 'data' : [ + form_widget(form.appendCategories, {'id': 'productAppendCategories'}), + form_widget(form.removeCategories, {'id': 'productRemoveCategories'}), + dataGrid.renderGrid('marello-product-categories-extend-grid', { product_id: entity.id ? entity.id : 0 }, { cssClass: 'inner-grid' }) + ] + }] + } + ]) %} + {% endif %} {% set additionalData = [] %} {% for child in form.children if child.vars.extra_field is defined and child.vars.extra_field %} diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/index.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/index.html.twig index 34191cfde..2e4c14f9d 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/index.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/index.html.twig @@ -5,7 +5,7 @@ {% set gridName = 'marello-products-grid' %} {% block navButtons %} - {% if resource_granted('marello_product_create') %} + {% if is_granted('marello_product_create') %} {{ UI.addButton({ 'path': path('marello_product_create'), 'entity_label': 'marello.product.entity_label'|trans diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/update.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/update.html.twig index 79c052c91..a8a5c94e5 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/update.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/update.html.twig @@ -1,10 +1,7 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} -{% form_theme form - 'MarelloPricingBundle:Form:fields.html.twig' - 'MarelloSupplierBundle:Form:fields.html.twig' - 'MarelloProductBundle:Form:fields.html.twig' -%} + {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% set gridName = 'marello-product-saleschannel-extended-grid' %} {% set entity = form.vars.value %} @@ -107,35 +104,47 @@ {% endset %} - {% set suppliers %} -
- {{ form_widget(form.suppliers) }} - {{ form_errors(form.suppliers) }} -
- {% endset %} + {% if marello_entity_has_attribute(entity, 'suppliers') %} + {% set suppliers %} +
+ {{ form_widget(form.suppliers) }} + {{ form_errors(form.suppliers) }} +
+ {% endset %} + {% endif %} {% set channelIds = marello_sales_get_saleschannel_ids(entity)|length > 0 ? marello_sales_get_saleschannel_ids(entity) : 0 %} + {% set generalData = [ + form_row(form.names), + form_row(form.sku), + form_row(form.status), + ] %} + {% if marello_entity_has_attribute(entity, 'weight') %} + {% set generalData = generalData|merge([form_row(form.weight)]) %} + {% endif %} + {% if marello_entity_has_attribute(entity, 'warranty') %} + {% set generalData = generalData|merge([form_row(form.warranty)]) %} + {% endif %} + {% if marello_entity_has_attribute(entity, 'manufacturingCode') %} + {% set generalData = generalData|merge([form_row(form.manufacturingCode)]) %} + {% endif %} + {% set generalData = generalData|merge([form_row(form.type)]) %} + {% if marello_entity_has_attribute(entity, 'image') %} + {% set generalData = generalData|merge([form_row(form.image)]) %} + {% endif %} + {% set dataBlocks = [ { 'title': 'marello.product.sections.general'|trans, 'subblocks': [{ 'title': 'General', - 'data': [ - form_row(form.name), - form_row(form.sku), - form_row(form.status), - form_row(form.weight), - form_row(form.warranty), - form_row(form.manufacturingCode), - form_row(form.type), - form_row(form.image), - ] + 'data': generalData }] }, { @@ -171,30 +180,38 @@ dataGrid.renderGrid(gridName, { product: form.vars.value.id ? : null, channels: channelIds }) ] }] - }, - { - 'title': 'marello.supplier.entity_plural_label'|trans, - 'class': 'active', - 'subblocks': [{ - 'title': '', - 'data': [ - suppliers - ] - }] - }, - { - 'title' : 'marello.catalog.category.entity_plural_label'|trans, - 'subblocks': [{ - 'title' : null, - 'useSpan': false, - 'data' : [ - form_widget(form.appendCategories, {'id': 'productAppendCategories'}), - form_widget(form.removeCategories, {'id': 'productRemoveCategories'}), - dataGrid.renderGrid('marello-product-categories-extend-grid', { product_id: entity.id ? entity.id : 0 }, { cssClass: 'inner-grid' }) - ] - }] } ] %} + {% if marello_entity_has_attribute(entity, 'suppliers') %} + {% set dataBlocks = dataBlocks|merge([ + { + 'title': 'marello.supplier.entity_plural_label'|trans, + 'class': 'active', + 'subblocks': [{ + 'title': '', + 'data': { + 'suppliers': suppliers + } + }] + } + ]) %} + {% endif %} + {% if marello_entity_has_attribute(entity, 'categories') %} + {% set dataBlocks = dataBlocks|merge([ + { + 'title' : 'marello.catalog.category.entity_plural_label'|trans, + 'subblocks': [{ + 'title' : null, + 'useSpan': false, + 'data' : [ + form_widget(form.appendCategories, {'id': 'productAppendCategories'}), + form_widget(form.removeCategories, {'id': 'productRemoveCategories'}), + dataGrid.renderGrid('marello-product-categories-extend-grid', { product_id: entity.id ? entity.id : 0 }, { cssClass: 'inner-grid' }) + ] + }] + } + ]) %} + {% endif %} {% set additionalData = [] %} {% for child in form.children if child.vars.extra_field is defined and child.vars.extra_field %} diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/view.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/view.html.twig index 7c2ba0fd7..2de6d4cdf 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/view.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/view.html.twig @@ -1,8 +1,9 @@ {% extends 'OroUIBundle:actions:view.html.twig' %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% import 'OroEntityConfigBundle::macros.html.twig' as entityConfig %} +{% import 'OroUIBundle::macros.html.twig' as UI %} -{% oro_title_set({params : {"%name%": entity.name } }) %} +{% oro_title_set({params : {"%name%": entity.defaultName.string|default('N/A'|trans) } }) %} {% set gridName = 'marello-products-grid' %} {% block pageHeader %} @@ -10,7 +11,7 @@ 'entity': entity, 'indexPath': path('marello_product_index'), 'indexLabel': 'marello.product.entity_plural_label'|trans, - 'entityTitle': entity.name|default('N/A') + 'entityTitle': entity.defaultName.string|default('N/A'|trans) } %} {{ parent() }} @@ -18,7 +19,7 @@ {% block navButtons %} {{ parent() }} - {% if resource_granted('EDIT', entity) %} + {% if is_granted('EDIT', entity) %} {{ UI.editButton({ 'path': path('marello_product_update', {'id': entity.id}), 'entity_label': 'marello.product.entity_label'|trans @@ -36,24 +37,27 @@ 'title': '' }) }} {% endset %} + {% set generalSubblocks = generalSubblocks|merge([{'data' : [productInformationWidget]}]) %} - {% set productImage = 'bundles/marelloproduct/img/no_image_available.jpg' | imagine_filter('product_view') %} - {% if entity.image is not null %} - {% set productImage = filtered_image_url(entity.image, 'product_view') %} - {% endif %} + {% if marello_entity_has_attribute(entity, 'image') %} + {% set productImage = 'bundles/marelloproduct/img/no_image_available.jpg' | imagine_filter('product_view') %} + {% if entity.image is not null %} + {% set productImage = filtered_image_url(entity.image, 'product_view') %} + {% endif %} - {% set productImageWidget %} -
-
-
-
- {{ entity.name }} + {% set productImageWidget %} +
+
+
+
+ {{ entity.name }} +
-
- {% endset %} - {% set generalSubblocks = generalSubblocks|merge([{'data' : [productInformationWidget] }, {'data' : [productImageWidget] }]) %} + {% endset %} + {% set generalSubblocks = generalSubblocks|merge([{'data' : [productImageWidget]}]) %} + {% endif %} {% set dataBlocks = [{ 'title': 'General'|trans, @@ -99,25 +103,29 @@
{% endset %} - {% set productSupplierWidget %} -
-
-
- {{ dataGrid.renderGrid('marello-product-suppliers-grid', {'suppliers': marello_supplier_get_supplier_ids(entity)}) }} + {% if marello_entity_has_attribute(entity, 'suppliers') %} + {% set productSupplierWidget %} +
+
+
+ {{ dataGrid.renderGrid('marello-product-suppliers-grid', {'suppliers': marello_supplier_get_supplier_ids(entity)}) }} +
-
- {% endset %} + {% endset %} + {% endif %} - {% set productCategoriesWidget %} -
-
-
- {{ dataGrid.renderGrid('marello-product-categories-grid', {'categories': marello_product_get_categories_ids(entity)}) }} + {% if marello_entity_has_attribute(entity, 'categories') %} + {% set productCategoriesWidget %} +
+
+
+ {{ dataGrid.renderGrid('marello-product-categories-grid', {'categories': marello_product_get_categories_ids(entity)}) }} +
-
- {% endset %} + {% endset %} + {% endif %} {% set dataBlocks = dataBlocks|merge([{ 'title' : 'marello.pricing.header.label'|trans, @@ -169,27 +177,31 @@ }] }]) %} - {% set dataBlocks = dataBlocks|merge([{ - 'title' : 'marello.supplier.entity_plural_label'|trans, - 'subblocks': [{ - 'title' : null, - 'useSpan': false, - 'data' : [ - productSupplierWidget - ] - }] - }]) %} + {% if marello_entity_has_attribute(entity, 'suppliers') %} + {% set dataBlocks = dataBlocks|merge([{ + 'title' : 'marello.supplier.entity_plural_label'|trans, + 'subblocks': [{ + 'title' : null, + 'useSpan': false, + 'data' : [ + productSupplierWidget + ] + }] + }]) %} + {% endif %} - {% set dataBlocks = dataBlocks|merge([{ - 'title' : 'marello.catalog.category.entity_plural_label'|trans, - 'subblocks': [{ - 'title' : null, - 'useSpan': false, - 'data' : [ - productCategoriesWidget - ] - }] - }]) %} + {% if marello_entity_has_attribute(entity, 'categories') %} + {% set dataBlocks = dataBlocks|merge([{ + 'title' : 'marello.catalog.category.entity_plural_label'|trans, + 'subblocks': [{ + 'title' : null, + 'useSpan': false, + 'data' : [ + productCategoriesWidget + ] + }] + }]) %} + {% endif %} {% set additionalData %}
diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/widget/info.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Product/widget/info.html.twig index f6e3475cb..c539276f6 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Product/widget/info.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Product/widget/info.html.twig @@ -4,11 +4,17 @@
{{ UI.renderProperty('marello.product.sku.label'|trans, product.sku) }} - {{ UI.renderProperty('marello.product.name.label'|trans, product.name) }} + {{ UI.renderProperty('marello.product.names.label'|trans, product.defaultName) }} {{ UI.renderProperty('marello.product.status.label'|trans, product.status) }} - {{ UI.renderProperty('marello.product.weight.label'|trans, product.weight) }} - {{ UI.renderProperty('marello.product.warranty.label'|trans, product.warranty) }} - {{ UI.renderProperty('marello.product.manufacturing_code.label'|trans, product.manufacturingCode) }} + {% if marello_entity_has_attribute(product, 'weight') %} + {{ UI.renderProperty('marello.product.weight.label'|trans, product.weight) }} + {% endif %} + {% if marello_entity_has_attribute(product, 'warranty') %} + {{ UI.renderProperty('marello.product.warranty.label'|trans, product.warranty) }} + {% endif %} + {% if marello_entity_has_attribute(product, 'manufacturingCode') %} + {{ UI.renderProperty('marello.product.manufacturing_code.label'|trans, product.manufacturingCode) }} + {% endif %}
diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Variant/Form/fields.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Variant/Form/fields.html.twig index ab847b3d4..fbcfb77ec 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Variant/Form/fields.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Variant/Form/fields.html.twig @@ -19,7 +19,7 @@ {% endblock %} -{% macro marello_product_collection_item_prototype(widget) %} +{% macro marello_product_collection_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -35,7 +35,7 @@ {% set name = widget.vars.full_name %} {% set disabled = widget.parent.vars.disabled %} {% endif %} - @@ -51,9 +51,12 @@ {% endmacro %} {% block marello_product_collection_widget %} + {% import _self as fields %} + {% spaceless %} + {% set widgetContainerAttributes = block('widget_container_attributes') %} {% if prototype is defined %} - {% set prototype_html = _self.marello_product_collection_item_prototype(form) %} + {% set prototype_html = fields.marello_product_collection_item_prototype(form, widgetContainerAttributes) %} {% endif %} {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} {% set id = id ~ '_collection' %} @@ -74,7 +77,7 @@ {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_product_collection_item_prototype(child) }} + {{ fields.marello_product_collection_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -87,7 +90,7 @@ {% endif %}
{% if handle_primary and (prototype is not defined or prototype.primary is defined) %} - {{ _self.oro_collection_validate_primary_js(_context) }} + {{ fields.oro_collection_validate_primary_js(_context) }} {% endif %} {% endspaceless %} {% endblock %} diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Variant/update.html.twig b/src/Marello/Bundle/ProductBundle/Resources/views/Variant/update.html.twig index 00f84e83a..1f2b55b23 100644 --- a/src/Marello/Bundle/ProductBundle/Resources/views/Variant/update.html.twig +++ b/src/Marello/Bundle/ProductBundle/Resources/views/Variant/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% set gridName = 'marello-product-variant-extended-grid' %} diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/ProductJsonApiTest.php b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/ProductJsonApiTest.php index 42f4f1873..9075b1ce1 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/ProductJsonApiTest.php +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/ProductJsonApiTest.php @@ -82,7 +82,10 @@ public function testCreateNewProduct() /** @var Product $product */ $productRepo = $this->getEntityManager()->getRepository(Product::class); $product = $productRepo->findOneBySku($responseContent->data->id); - $this->assertEquals($product->getName(), $responseContent->data->attributes->name); + $this->assertEquals( + $product->getDenormalizedDefaultName(), + $responseContent->data->attributes->denormalizedDefaultName + ); } /** @@ -106,6 +109,10 @@ public function testUpdateProduct() /** @var Product $product */ $productRepo = $this->getEntityManager()->getRepository(Product::class); $product = $productRepo->findOneBySku($responseContent->data->id); - $this->assertEquals($product->getName(), $responseContent->data->attributes->name); + $this->assertEquals($product->getManufacturingCode(), $responseContent->data->attributes->manufacturingCode); + $this->assertEquals( + $product->getDenormalizedDefaultName(), + $responseContent->data->attributes->denormalizedDefaultName + ); } } diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_create.yml b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_create.yml index 307f4a199..5d782c412 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_create.yml +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_create.yml @@ -2,12 +2,18 @@ data: type: marelloproducts id: 'my-sku' attributes: - cost: 10 manufacturingCode: '1234567890987654321' - name: 'My name' warranty: 10 weight: 10.000 relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'names-1' + - + type: localizedfallbackvalues + id: 'names-2' saleschannels: data: - @@ -27,3 +33,26 @@ data: data: type: marellotaxcodes id: 'taxCode->code)>' +included: + - + type: localizedfallbackvalues + id: 'names-1' + attributes: + fallback: null + string: 'Test product' + text: null + relationships: + localization: + data: null + - + type: localizedfallbackvalues + id: 'names-2' + attributes: + fallback: null + string: 'Product in spanish' + text: null + relationships: + localization: + data: + type: localizations + id: 'getId())>' \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_update.yml b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_update.yml index 033cc6b34..d232ed4b4 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_update.yml +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/requests/product_update.yml @@ -2,4 +2,26 @@ data: type: marelloproducts id: 'sku)>' attributes: - name: 'new_name' + manufacturingCode: 'new code' + relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'getId())>' + - + type: localizedfallbackvalues + id: 'names-new' +included: + - + type: localizedfallbackvalues + id: 'names-new' + attributes: + fallback: null + string: 'Product in spanish' + text: null + relationships: + localization: + data: + type: localizations + id: 'getId())>' \ No newline at end of file diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/cget_product_list.yml b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/cget_product_list.yml index 349d59640..369e61649 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/cget_product_list.yml +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/cget_product_list.yml @@ -4,14 +4,18 @@ data: id: p1 attributes: productId: '@product1->id' - name: product1 + denormalizedDefaultName: 'product1' manufacturingCode: null productType: null - cost: null weight: 1 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses @@ -45,14 +49,18 @@ data: id: p2 attributes: productId: '@product2->id' - name: 'product 2' + denormalizedDefaultName: 'product2' manufacturingCode: null productType: null - cost: null weight: 2 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses @@ -92,14 +100,18 @@ data: id: p3 attributes: productId: '@product3->id' - name: 'product 3' + denormalizedDefaultName: 'product3' manufacturingCode: null productType: null - cost: null weight: 5 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses @@ -139,14 +151,18 @@ data: id: p4 attributes: productId: '@product4->id' - name: 'product 4' + denormalizedDefaultName: 'product4' manufacturingCode: null productType: null - cost: null weight: 10 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses @@ -192,14 +208,18 @@ data: id: p5 attributes: productId: '@product5->id' - name: product5 + denormalizedDefaultName: 'product5' manufacturingCode: null productType: null - cost: null weight: 1 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses @@ -233,14 +253,18 @@ data: id: p6 attributes: productId: '@product6->id' - name: product6 + denormalizedDefaultName: 'product6' manufacturingCode: null productType: null - cost: null weight: 1 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_id.yml b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_id.yml index c948eda4c..1067f3f60 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_id.yml +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_id.yml @@ -3,14 +3,18 @@ data: id: p1 attributes: productId: '@product1->id' - name: product1 + denormalizedDefaultName: product1 manufacturingCode: null productType: null - cost: null weight: 1 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_sku.yml b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_sku.yml index 53360383f..78a210746 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_sku.yml +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Api/responses/get_product_by_sku.yml @@ -4,14 +4,18 @@ data: id: p1 attributes: productId: '@product1->id' - name: product1 + denormalizedDefaultName: product1 manufacturingCode: null productType: null - cost: null weight: 1 warranty: null data: null relationships: + names: + data: + - + type: localizedfallbackvalues + id: 'id)>' productstatus: data: type: marelloproductstatuses diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductAttributeControllerTest.php b/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductAttributeControllerTest.php index 0cf8e7101..343ecc2a4 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductAttributeControllerTest.php +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductAttributeControllerTest.php @@ -4,12 +4,9 @@ use Symfony\Component\HttpFoundation\Response; -use Oro\Bundle\EntityConfigBundle\Tests\Functional\Controller\AttributeControllerTest; +use Oro\Bundle\EntityExtendBundle\Tests\Functional\AbstractConfigControllerTest; -/** - * @outputBuffering enabled - */ -class ProductAttributeControllerTest extends AttributeControllerTest +class ProductAttributeControllerTest extends AbstractConfigControllerTest { const PRODUCT_ENTITY_ALIAS = 'marelloproduct'; @@ -30,26 +27,4 @@ protected function getTestEntityAlias() { return self::PRODUCT_ENTITY_ALIAS; } - - /** - * {@inheritdoc} - */ - public function testCreateImage() - { - // skip test during hardcoded entity classname in dependent functions - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * {@inheritdoc} - */ - public function testCreateFile() - { - // skip test during hardcoded entity classname in dependent functions - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } } diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductControllerTest.php b/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductControllerTest.php index 27bd5e7a9..e1bdf1b7c 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductControllerTest.php +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/Controller/ProductControllerTest.php @@ -3,12 +3,13 @@ namespace Marello\Bundle\ProductBundle\Tests\Functional\Controller; use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\ProductBundle\Model\ProductType; +use Marello\Bundle\ProductBundle\Migrations\Data\ORM\LoadDefaultAttributeFamilyData; use Marello\Bundle\ProductBundle\Provider\ProductTypesProvider; use Marello\Bundle\ProductBundle\Tests\Functional\DataFixtures\LoadProductData; use Marello\Bundle\SalesBundle\Tests\Functional\DataFixtures\LoadSalesData; use Marello\Bundle\SupplierBundle\Tests\Functional\DataFixtures\LoadSupplierData; use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxCodeData; +use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily; use Oro\Bundle\TestFrameworkBundle\Test\WebTestCase; use Symfony\Component\DomCrawler\Form; use Symfony\Component\HttpFoundation\Response; @@ -52,10 +53,16 @@ public function testCreateProduct() $productTypesProvider = $this->getContainer()->get('marello_product.provider.product_types'); if (count($productTypesProvider->getProductTypes()) > 1) { $defaultProductType = $productTypesProvider->getProductType('simple')->getName(); + $em = $this->getContainer()->get('doctrine.orm.entity_manager'); + /** @var AttributeFamily $attributeFamily */ + $attributeFamily = $em + ->getRepository(AttributeFamily::class) + ->findOneBy(['code' => LoadDefaultAttributeFamilyData::DEFAULT_FAMILY_CODE]); $form = $crawler->selectButton('Continue')->form(); $formValues = $form->getPhpValues(); $formValues['input_action'] = 'marello_product_create'; $formValues['marello_product_step_one']['type'] = $defaultProductType; + $formValues['marello_product_step_one']['attributeFamily'] = $attributeFamily->getId(); $this->client->followRedirects(true); $crawler = $this->client->request( 'POST', @@ -69,7 +76,7 @@ public function testCreateProduct() $form = $crawler->selectButton('Save and Close')->form(); $formValues = $form->getPhpValues(); - $formValues['marello_product_form']['name'] = $name; + $formValues['marello_product_form']['names']['values']['default'] = $name; $formValues['marello_product_form']['sku'] = $sku; $formValues['marello_product_form']['status'] = 'enabled'; $formValues['marello_product_form']['addSalesChannels'] @@ -137,7 +144,7 @@ public function testUpdateProductSuppliers() 'input_action' => 'save_and_stay', 'marello_product_form' => [ '_token' => $form['marello_product_form[_token]']->getValue(), - 'name' => $form['marello_product_form[name]']->getValue(), + 'names' => ['values' => ['default' => $form['marello_product_form[names][values][default]']->getValue()]], 'sku' => $form['marello_product_form[sku]']->getValue(), 'status' => $form['marello_product_form[status]']->getValue(), 'suppliers' => $productSuppliers @@ -185,7 +192,7 @@ public function testUpdateProductTaxCodes() 'input_action' => 'save_and_stay', 'marello_product_form' => [ '_token' => $form['marello_product_form[_token]']->getValue(), - 'name' => $form['marello_product_form[name]']->getValue(), + 'names' => ['values' => ['default' => $form['marello_product_form[names][values][default]']->getValue()]], 'sku' => $form['marello_product_form[sku]']->getValue(), 'status' => $form['marello_product_form[status]']->getValue(), 'taxCode' => $taxCode, @@ -230,7 +237,7 @@ public function testUpdateProduct($name) /** @var Form $form */ $form = $crawler->selectButton('Save and Close')->form(); $name = 'name' . self::generateRandomString(); - $form['marello_product_form[name]'] = $name; + $form['marello_product_form[names][values][default]'] = $name; $form['marello_product_form[removeSalesChannels]'] = $this->getReference(LoadSalesData::CHANNEL_1_REF)->getId(); $form['marello_product_form[addSalesChannels]'] diff --git a/src/Marello/Bundle/ProductBundle/Tests/Functional/DataFixtures/LoadProductData.php b/src/Marello/Bundle/ProductBundle/Tests/Functional/DataFixtures/LoadProductData.php index c279e0c7f..6adc549f4 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Functional/DataFixtures/LoadProductData.php +++ b/src/Marello/Bundle/ProductBundle/Tests/Functional/DataFixtures/LoadProductData.php @@ -20,6 +20,10 @@ use Marello\Bundle\SalesBundle\Tests\Functional\DataFixtures\LoadSalesData; use Marello\Bundle\SupplierBundle\Tests\Functional\DataFixtures\LoadSupplierData; use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily; +use Oro\Bundle\EntityConfigBundle\Tests\Functional\DataFixtures\LoadAttributeFamilyData; +use Oro\Bundle\LocaleBundle\Entity\Localization; +use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue; +use Oro\Bundle\LocaleBundle\Tests\Functional\DataFixtures\LoadLocalizationData; class LoadProductData extends AbstractFixture implements DependentFixtureInterface { @@ -41,7 +45,9 @@ class LoadProductData extends AbstractFixture implements DependentFixtureInterfa /** @var array $data */ protected $data = [ self::PRODUCT_1_REF => [ - 'name' => 'product1', + 'names' => [ + ['reference' => 'product1.names.default', 'string' => 'product1'] + ], 'sku' => 'p1', 'price' => 10, 'weight' => 1.00, @@ -58,7 +64,9 @@ class LoadProductData extends AbstractFixture implements DependentFixtureInterfa ] ], self::PRODUCT_2_REF => [ - 'name' => 'product 2', + 'names' => [ + ['reference' => 'product2.names.default', 'string' => 'product2'] + ], 'sku' => 'p2', 'price' => 25, 'weight' => 2.00, @@ -82,7 +90,9 @@ class LoadProductData extends AbstractFixture implements DependentFixtureInterfa ] ], self::PRODUCT_3_REF => [ - 'name' => 'product 3', + 'names' => [ + ['reference' => 'product3.names.default', 'string' => 'product3'] + ], 'sku' => 'p3', 'price' => 50, 'weight' => 5.00, @@ -106,7 +116,9 @@ class LoadProductData extends AbstractFixture implements DependentFixtureInterfa ] ], self::PRODUCT_4_REF => [ - 'name' => 'product 4', + 'names' => [ + ['reference' => 'product4.names.default', 'string' => 'product4'] + ], 'sku' => 'p4', 'price' => 100, 'weight' => 10.00, @@ -127,7 +139,9 @@ class LoadProductData extends AbstractFixture implements DependentFixtureInterfa ] ], self::PRODUCT_5_REF => [ - 'name' => 'product5', + 'names' => [ + ['reference' => 'product5.names.default', 'string' => 'product5'] + ], 'sku' => 'p5', 'price' => 10, 'weight' => 1.00, @@ -144,7 +158,9 @@ class LoadProductData extends AbstractFixture implements DependentFixtureInterfa ] ], self::PRODUCT_6_REF => [ - 'name' => 'product6', + 'names' => [ + ['reference' => 'product6.names.default', 'string' => 'product6'] + ], 'sku' => 'p6', 'price' => 10, 'weight' => 1.00, @@ -165,6 +181,8 @@ class LoadProductData extends AbstractFixture implements DependentFixtureInterfa public function getDependencies() { return [ + LoadLocalizationData::class, + LoadAttributeFamilyData::class, LoadSalesData::class, LoadSupplierData::class, LoadTaxCodeData::class @@ -210,7 +228,11 @@ private function createProduct(array $data) { $product = new Product(); $product->setSku($data['sku']); - $product->setName($data['name']); + if (!empty($data['names'])) { + foreach ($data['names'] as $name) { + $product->addName($this->createLocalizedValue($name)); + } + } $product->setOrganization($this->defaultOrganization); $product->setWeight($data['weight']); @@ -354,4 +376,30 @@ protected function addProductSuppliers(Product $product, array $data) $product->setPreferredSupplier($preferredSupplier); } } + + /** + * @param array $name + * @return LocalizedFallbackValue + */ + protected function createLocalizedValue(array $name) + { + $value = new LocalizedFallbackValue(); + if (array_key_exists('localization', $name)) { + /** @var Localization $localization */ + $localization = $this->getReference($name['localization']); + $value->setLocalization($localization); + } + if (array_key_exists('fallback', $name)) { + $value->setFallback($name['fallback']); + } + if (array_key_exists('string', $name)) { + $value->setString($name['string']); + } + if (array_key_exists('text', $name)) { + $value->setText($name['text']); + } + $this->setReference($name['reference'], $value); + + return $value; + } } diff --git a/src/Marello/Bundle/ProductBundle/Tests/Unit/Entity/ProductTest.php b/src/Marello/Bundle/ProductBundle/Tests/Unit/Entity/ProductTest.php index 171c63191..25619d778 100644 --- a/src/Marello/Bundle/ProductBundle/Tests/Unit/Entity/ProductTest.php +++ b/src/Marello/Bundle/ProductBundle/Tests/Unit/Entity/ProductTest.php @@ -2,6 +2,7 @@ namespace Marello\Bundle\ProductBundle\Tests\Unit\Entity; +use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue; use PHPUnit\Framework\TestCase; use Oro\Component\Testing\Unit\EntityTrait; @@ -39,12 +40,10 @@ public function testAccessors() { $this->assertPropertyAccessors(new Product(), [ ['id', 42], - ['name', 'some string'], ['sku', 'some string'], ['manufacturingCode', 'some string'], ['status', new ProductStatus('active')], ['type', 'some string'], - ['cost', 'some string'], ['weight', 3.1415926], ['warranty', 42], ['organization', new Organization()], @@ -57,6 +56,7 @@ public function testAccessors() ['updatedAt', new \DateTime()] ]); $this->assertPropertyCollections(new Product(), [ + ['names', new LocalizedFallbackValue()], ['prices', new AssembledPriceList()], ['channels', new SalesChannel()], ['channelPrices', new AssembledChannelPriceList()], diff --git a/src/Marello/Bundle/ProductBundle/Twig/DynamicFieldsExtension.php b/src/Marello/Bundle/ProductBundle/Twig/DynamicFieldsExtension.php new file mode 100644 index 000000000..eea7753c8 --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Twig/DynamicFieldsExtension.php @@ -0,0 +1,75 @@ +attributeManager = $attributeManager; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return self::NAME; + } + + /** + * Returns a list of functions to add to the existing list. + * + * @return array An array of functions + */ + public function getFunctions() + { + return [ + new TwigFunction( + 'marello_entity_has_attribute', + [$this, 'hasAttribute'] + ) + ]; + } + + /** + * @param AttributeFamilyAwareInterface $entity + * @param string $fieldName + * @return bool + */ + public function hasAttribute(AttributeFamilyAwareInterface $entity, $fieldName) + { + $groupsData = $this->attributeManager->getGroupsWithAttributes($entity->getAttributeFamily()); + foreach ($groupsData as $groupsDatum) { + /** @var FieldConfigModel $attribute */ + foreach ($groupsDatum['attributes'] as $attribute) { + if ($attribute) { + $field = $attribute->getFieldName(); + if ($field === $fieldName) { + return true; + } + } + } + } + + return false; + } +} diff --git a/src/Marello/Bundle/ProductBundle/Twig/ProductExtension.php b/src/Marello/Bundle/ProductBundle/Twig/ProductExtension.php index 19bc803f5..f07ae8e2c 100644 --- a/src/Marello/Bundle/ProductBundle/Twig/ProductExtension.php +++ b/src/Marello/Bundle/ProductBundle/Twig/ProductExtension.php @@ -2,28 +2,31 @@ namespace Marello\Bundle\ProductBundle\Twig; -use Marello\Bundle\ProductBundle\Entity\Repository\ProductRepository; -use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; - +use Marello\Bundle\CatalogBundle\Provider\CategoriesIdsProvider; use Marello\Bundle\ProductBundle\Entity\Product; +use Marello\Bundle\ProductBundle\Entity\Repository\ProductRepository; use Marello\Bundle\SalesBundle\Provider\ChannelProvider; -use Marello\Bundle\CatalogBundle\Provider\CategoriesIdsProvider; +use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class ProductExtension extends \Twig_Extension +class ProductExtension extends AbstractExtension { const NAME = 'marello_product'; /** - * @var ChannelProvider $channelProvider + * @var ChannelProvider */ protected $channelProvider; /** - * @var CategoriesIdsProvider $categoriesIdsProvider + * @var CategoriesIdsProvider */ protected $categoriesIdsProvider; - /** @var DoctrineHelper $doctrineHelper */ + /** + * @var DoctrineHelper + */ private $doctrineHelper; /** @@ -54,15 +57,15 @@ public function getName() public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_sales_get_saleschannel_ids', [$this, 'getSalesChannelsIds'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_product_get_categories_ids', [$this, 'getCategoriesIds'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_get_product_by_sku', [$this, 'getProductBySku'] ) diff --git a/src/Marello/Bundle/ProductBundle/Validator/Constraints/ProductSupplierRelationsDropship.php b/src/Marello/Bundle/ProductBundle/Validator/Constraints/ProductSupplierRelationsDropship.php new file mode 100644 index 000000000..ae6eda3fc --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Validator/Constraints/ProductSupplierRelationsDropship.php @@ -0,0 +1,27 @@ +doctrine = $doctrine; + } + + /** + * Checks if the passed entity is unique in collection. + * @param mixed $entity + * @param Constraint $constraint + * @throws UnexpectedTypeException + */ + public function validate($entity, Constraint $constraint) + { + if ($entity instanceof Product && $entity->getId()) { + $existingSupplierRelationsCollection = $this->doctrine + ->getManagerForClass(ProductSupplierRelation::class) + ->getRepository(ProductSupplierRelation::class) + ->findBy(['product' => $entity->getId()]); + $existingSupplierRelations = $this->makeSupplierRelationsByIdsArray($existingSupplierRelationsCollection); + $supplierRelations = $this->makeSupplierRelationsByIdsArray($entity->getSuppliers()->toArray()); + foreach ($existingSupplierRelations as $id => $existingSupplierRelation) { + if ((!isset($supplierRelations[$id]) && $existingSupplierRelation->getCanDropship()) || + (isset($supplierRelations[$id]) && !$supplierRelations[$id]->getCanDropship())) { + $this->onProductDropshipOff($existingSupplierRelation, $constraint); + } + } + } + } + + /** + * @param array $supplierRelations + * @return array + */ + private function makeSupplierRelationsByIdsArray(array $supplierRelations) + { + $supplierRelationsByIds = []; + foreach ($supplierRelations as $supplierRelation) { + if ($id = $supplierRelation->getId()) { + $supplierRelationsByIds[$id] = $supplierRelation; + } + } + + return $supplierRelationsByIds; + } + + /** + * @param ProductSupplierRelation $productSupplierRelation + * @param Constraint $constraint + */ + private function onProductDropshipOff(ProductSupplierRelation $productSupplierRelation, Constraint $constraint) + { + $warehouse = $this->getWarehouse($productSupplierRelation->getSupplier()); + if ($warehouse) { + $inventoryItem = $this->doctrine + ->getManagerForClass(InventoryItem::class) + ->getRepository(InventoryItem::class) + ->findOneByProduct($productSupplierRelation->getProduct()); + + $inventoryLevel = $inventoryItem->getInventoryLevel($warehouse); + if ($inventoryLevel) { + if ($inventoryLevel->getInventoryQty() > 0 || $inventoryLevel->getAllocatedInventoryQty() > 0) { + $this->context->buildViolation($constraint->message) + ->atPath('suppliers') + ->addViolation(); + } + } + } + } + + /** + * @param Supplier $supplier + * @return Warehouse + */ + private function getWarehouse(Supplier $supplier) + { + $warehouseType = $this->doctrine + ->getManagerForClass(WarehouseType::class) + ->getRepository(WarehouseType::class) + ->find(WarehouseTypeProviderInterface::WAREHOUSE_TYPE_EXTERNAL); + $warehouse = $this->doctrine + ->getManagerForClass(Warehouse::class) + ->getRepository(Warehouse::class) + ->findOneBy([ + 'code' => sprintf('%s_external_warehouse', str_replace(' ', '_', strtolower($supplier->getName()))), + 'warehouseType' => $warehouseType + ]); + + return $warehouse; + } +} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Controller/PurchaseOrderController.php b/src/Marello/Bundle/PurchaseOrderBundle/Controller/PurchaseOrderController.php index 3d8c2d989..a15c6e4fa 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Controller/PurchaseOrderController.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Controller/PurchaseOrderController.php @@ -9,20 +9,22 @@ use Marello\Bundle\PurchaseOrderBundle\Form\Type\PurchaseOrderCreateStepOneType; use Marello\Bundle\PurchaseOrderBundle\Form\Type\PurchaseOrderCreateStepTwoType; use Marello\Bundle\SupplierBundle\Entity\Supplier; - +use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; -use Oro\Bundle\SecurityBundle\Annotation\Acl; +use Symfony\Component\Routing\Annotation\Route; -class PurchaseOrderController extends Controller +class PurchaseOrderController extends AbstractController { /** - * @Config\Route("/", name="marello_purchaseorder_purchaseorder_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_purchaseorder_purchaseorder_index" + * ) + * @Config\Template("MarelloPurchaseOrderBundle:PurchaseOrder:index.html.twig") * @AclAncestor("marello_purchase_order_view") */ public function indexAction() @@ -31,8 +33,12 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_purchaseorder_purchaseorder_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_purchaseorder_purchaseorder_view" + * ) + * @Config\Template("MarelloPurchaseOrderBundle:PurchaseOrder:view.html.twig") * @AclAncestor("marello_purchase_order_view") * * @param PurchaseOrder $purchaseOrder @@ -47,7 +53,10 @@ public function viewAction(PurchaseOrder $purchaseOrder) } /** - * @Config\Route("/select-products", name="marello_purchaseorder_purchaseorder_selectproducts") + * @Route( + * path="/select-products", + * name="marello_purchaseorder_purchaseorder_selectproducts" + * ) * @Config\Template * @AclAncestor("marello_purchase_order_create") */ @@ -57,7 +66,10 @@ public function selectProductsAction() } /** - * @Config\Route("/create", name="marello_purchaseorder_purchaseorder_create") + * @Route( + * path="/create", + * name="marello_purchaseorder_purchaseorder_create" + * ) * @Config\Template("MarelloPurchaseOrderBundle:PurchaseOrder:createStepOne.html.twig") * @AclAncestor("marello_purchase_order_create") * @@ -71,9 +83,13 @@ public function createAction(Request $request) } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_purchaseorder_purchaseorder_update") + * @Route( + * path="/update/{id}", + * requirements={"id"="\d+"}, + * name="marello_purchaseorder_purchaseorder_update" + * ) * @AclAncestor("marello_purchase_order_update") - * @Config\Template + * @Config\Template("MarelloPurchaseOrderBundle:PurchaseOrder:update.html.twig") * * @param PurchaseOrder $purchaseOrder * @@ -85,7 +101,10 @@ public function updateAction(PurchaseOrder $purchaseOrder) } /** - * @Config\Route("/create/step-two", name="marello_purchaseorder_purchaseorder_create_step_two") + * @Route( + * path="/create/step-two", + * name="marello_purchaseorder_purchaseorder_create_step_two" + * ) * @Config\Template("MarelloPurchaseOrderBundle:PurchaseOrder:createStepTwo.html.twig") * @AclAncestor("marello_purchase_order_create") * @@ -219,14 +238,16 @@ protected function update(PurchaseOrder $purchaseOrder) } /** - * @Config\Route( - * "/widget/products/{id}", + * @param PurchaseOrder|null $purchaseOrder + * @Route( + * path="/widget/products/{id}", * name="marello_purchase_order_widget_products_by_supplier", * requirements={"id"="\d+"}, * defaults={"id"=0} * ) * @AclAncestor("marello_product_view") - * @Config\Template() + * @Config\Template("MarelloPurchaseOrderBundle:PurchaseOrder/widget:productsBySupplier.html.twig") + * @return array */ public function productsBySupplierAction(PurchaseOrder $purchaseOrder = null) { @@ -244,10 +265,13 @@ public function productsBySupplierAction(PurchaseOrder $purchaseOrder = null) } /** - * @Config\Route("/supplier-product-price/{productId}/{supplierId}", name="marello_purchase_order_supplier_product_price") + * @Route( + * path="/supplier-product-price/{productId}/{supplierId}", + * methods={"GET"}, + * name="marello_purchase_order_supplier_product_price" + * ) * @Config\ParamConverter("product", options={"mapping": {"productId" : "id"}}) * @Config\ParamConverter("supplier", options={"mapping": {"supplierId" : "id"}}) - * @Config\Method({"GET"}) * @AclAncestor("marello_product_view") * * @param Product $product diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Cron/PurchaseOrderAdviceCommand.php b/src/Marello/Bundle/PurchaseOrderBundle/Cron/PurchaseOrderAdviceCommand.php index ebe69989b..830cba549 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Cron/PurchaseOrderAdviceCommand.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Cron/PurchaseOrderAdviceCommand.php @@ -3,7 +3,7 @@ namespace Marello\Bundle\PurchaseOrderBundle\Cron; use Marello\Bundle\NotificationBundle\Email\SendProcessor; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrder; use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrderItem; diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Entity/PurchaseOrderItem.php b/src/Marello/Bundle/PurchaseOrderBundle/Entity/PurchaseOrderItem.php index b435024ee..ca8daf4b6 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Entity/PurchaseOrderItem.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Entity/PurchaseOrderItem.php @@ -270,14 +270,14 @@ public function getOrder() } /** - * @param ProductInterface $product + * @param ProductInterface|Product $product * * @return $this */ public function setProduct(ProductInterface $product) { $this->product = $product; - $this->productName = $this->product->getName(); + $this->productName = $this->product->getDenormalizedDefaultName(); $this->productSku = $this->product->getSku(); return $this; diff --git a/src/Marello/Bundle/PurchaseOrderBundle/EventListener/Datagrid/PurchaseOrderGridListener.php b/src/Marello/Bundle/PurchaseOrderBundle/EventListener/Datagrid/PurchaseOrderGridListener.php index 3167f01c7..a94ea0e75 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/EventListener/Datagrid/PurchaseOrderGridListener.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/EventListener/Datagrid/PurchaseOrderGridListener.php @@ -2,12 +2,10 @@ namespace Marello\Bundle\PurchaseOrderBundle\EventListener\Datagrid; +use Doctrine\ORM\EntityManager; use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrder; -use Oro\Bundle\DataGridBundle\Event\BuildAfter; use Oro\Bundle\DataGridBundle\Event\BuildBefore; -use Oro\Bundle\FilterBundle\Form\Type\Filter\TextFilterType; use Oro\Bundle\WorkflowBundle\Model\WorkflowManager; -use Doctrine\ORM\EntityManager; class PurchaseOrderGridListener { diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Form/EventListener/PurchaseOrderItemSubscriber.php b/src/Marello/Bundle/PurchaseOrderBundle/Form/EventListener/PurchaseOrderItemSubscriber.php index c71e67a18..2f6910b49 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Form/EventListener/PurchaseOrderItemSubscriber.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Form/EventListener/PurchaseOrderItemSubscriber.php @@ -6,7 +6,7 @@ use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormError; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class PurchaseOrderItemSubscriber implements EventSubscriberInterface { diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateHandler.php b/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateHandler.php index 288ffad58..093f66d60 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateHandler.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateHandler.php @@ -6,6 +6,7 @@ use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrder; use Oro\Bundle\FormBundle\Form\Handler\RequestHandlerTrait; use Oro\Bundle\OrganizationBundle\Entity\Organization; +use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationAwareTokenInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -56,9 +57,13 @@ public function handle() /* * Get organization of currently logged in user, or use first one. */ + $organization = null; if ($token = $this->tokenStorage->getToken()) { - $organization = $token->getOrganizationContext(); - } else { + if ($token instanceof OrganizationAwareTokenInterface) { + $organization = $token->getOrganization(); + } + } + if (!$organization) { $organization = $this->doctrine->getRepository(Organization::class)->getFirst(); } diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateStepOneHandler.php b/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateStepOneHandler.php index bc7f28fbc..f77b99cb0 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateStepOneHandler.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderCreateStepOneHandler.php @@ -2,16 +2,9 @@ namespace Marello\Bundle\PurchaseOrderBundle\Form\Handler; -use Doctrine\Bundle\DoctrineBundle\Registry; -use Marello\Bundle\ProductBundle\Entity\Product; -use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrder; -use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrderItem; -use Marello\Bundle\PurchaseOrderBundle\Form\Type\PurchaseOrderCreateStepOneType; use Oro\Bundle\FormBundle\Form\Handler\RequestHandlerTrait; -use Oro\Bundle\OrganizationBundle\Entity\Organization; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; class PurchaseOrderCreateStepOneHandler { diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderUpdateHandler.php b/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderUpdateHandler.php index 1373c0653..7d3e85ee5 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderUpdateHandler.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Form/Handler/PurchaseOrderUpdateHandler.php @@ -37,7 +37,7 @@ public function __construct( } /** - * + * @param PurchaseOrder $entity * @return bool */ public function process(PurchaseOrder $entity) diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/MarelloPurchaseOrderBundleInstaller.php b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/MarelloPurchaseOrderBundleInstaller.php index 81aaf7070..5349baf72 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/MarelloPurchaseOrderBundleInstaller.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/MarelloPurchaseOrderBundleInstaller.php @@ -24,7 +24,7 @@ class MarelloPurchaseOrderBundleInstaller implements */ public function getMigrationVersion() { - return 'v1_3_1'; + return 'v1_3_3'; } /** diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_1/MarelloPurchaseOrderBundle.php b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_1/MarelloPurchaseOrderBundle.php index 67ee1463c..bb339cf8d 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_1/MarelloPurchaseOrderBundle.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_1/MarelloPurchaseOrderBundle.php @@ -3,10 +3,8 @@ namespace Marello\Bundle\PurchaseOrderBundle\Migrations\Schema\v1_1; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\MigrationBundle\Migration\QueryBag; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtension; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtensionAwareInterface; use Oro\Bundle\MigrationBundle\Migration\Migration; +use Oro\Bundle\MigrationBundle\Migration\QueryBag; /** * @SuppressWarnings(PHPMD.TooManyMethods) diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_1/MarelloPurchaseOrderBundle.php b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_1/MarelloPurchaseOrderBundle.php index 6dad9ce21..177eba177 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_1/MarelloPurchaseOrderBundle.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_1/MarelloPurchaseOrderBundle.php @@ -31,6 +31,7 @@ public function up(Schema $schema, QueryBag $queries) * Creates supplier column and sets the value of the current purchase orders * * @param Schema $schema + * @param QueryBag $queries */ protected function updatePurchaseOrderTable(Schema $schema, QueryBag $queries) { diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_2/MarelloPurchaseOrderBundle.php b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_2/MarelloPurchaseOrderBundle.php index 75671d169..e9177a765 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_2/MarelloPurchaseOrderBundle.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_2/MarelloPurchaseOrderBundle.php @@ -3,10 +3,8 @@ namespace Marello\Bundle\PurchaseOrderBundle\Migrations\Schema\v1_2_2; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\MigrationBundle\Migration\QueryBag; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtension; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtensionAwareInterface; use Oro\Bundle\MigrationBundle\Migration\Migration; +use Oro\Bundle\MigrationBundle\Migration\QueryBag; /** * @SuppressWarnings(PHPMD.TooManyMethods) @@ -19,7 +17,7 @@ class MarelloPurchaseOrderBundle implements Migration */ public function up(Schema $schema, QueryBag $queries) { - $this->updatePurchaseOrderTable($schema, $queries); + $this->updatePurchaseOrderTable($schema); } /** @@ -27,7 +25,7 @@ public function up(Schema $schema, QueryBag $queries) * * @param Schema $schema */ - protected function updatePurchaseOrderTable(Schema $schema, QueryBag $queries) + protected function updatePurchaseOrderTable(Schema $schema) { $table = $schema->getTable('marello_purchase_order'); diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_3/MarelloPurchaseOrderBundle.php b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_3/MarelloPurchaseOrderBundle.php index c32a38070..debb2da8c 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_3/MarelloPurchaseOrderBundle.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_2_3/MarelloPurchaseOrderBundle.php @@ -3,10 +3,8 @@ namespace Marello\Bundle\PurchaseOrderBundle\Migrations\Schema\v1_2_3; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\MigrationBundle\Migration\QueryBag; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtension; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtensionAwareInterface; use Oro\Bundle\MigrationBundle\Migration\Migration; +use Oro\Bundle\MigrationBundle\Migration\QueryBag; /** * @SuppressWarnings(PHPMD.TooManyMethods) @@ -19,7 +17,7 @@ class MarelloPurchaseOrderBundle implements Migration */ public function up(Schema $schema, QueryBag $queries) { - $this->updatePurchaseOrderTable($schema, $queries); + $this->updatePurchaseOrderTable($schema); } /** @@ -27,7 +25,7 @@ public function up(Schema $schema, QueryBag $queries) * * @param Schema $schema */ - protected function updatePurchaseOrderTable(Schema $schema, QueryBag $queries) + protected function updatePurchaseOrderTable(Schema $schema) { $table = $schema->getTable('marello_purchase_order'); diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_2/MarelloPurchaseOrderBundle.php b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_2/MarelloPurchaseOrderBundle.php new file mode 100644 index 000000000..72d9cb2b2 --- /dev/null +++ b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_2/MarelloPurchaseOrderBundle.php @@ -0,0 +1,35 @@ +updatePurchaseOrderTable($schema); + } + + /** + * @param Schema $schema + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function updatePurchaseOrderTable(Schema $schema) + { + $table = $schema->getTable('marello_purchase_order'); + if (!$table->hasColumn('data')) { + $table->addColumn('data', 'json_array', ['notnull' => false, 'comment' => '(DC2Type:json_array)']); + } + } +} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_3/MarelloPurchaseOrderBundle.php b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_3/MarelloPurchaseOrderBundle.php new file mode 100644 index 000000000..6ad3ec5b3 --- /dev/null +++ b/src/Marello/Bundle/PurchaseOrderBundle/Migrations/Schema/v1_3_3/MarelloPurchaseOrderBundle.php @@ -0,0 +1,49 @@ +updatePurchaseOrderItemTable($schema); + } + /** + * @param Schema $schema + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function updatePurchaseOrderItemTable(Schema $schema) + { + $table = $schema->getTable('marello_purchase_order_item'); + if (!$table->hasColumn('organization_id')) { + $table->addColumn('organization_id', 'integer', ['notnull' => false]); + } + + if (!$table->hasIndex('IDX_3483BD8632C8A3DE')) { + // add index to organization column + $table->addIndex(['organization_id']); + } + + if (!$table->hasForeignKey('FK_3483BD8632C8A3DE')) { + // add foreign key constraint + $table->addForeignKeyConstraint( + $schema->getTable('oro_organization'), + ['organization_id'], + ['id'], + ['onDelete' => 'SET NULL', 'onUpdate' => null] + ); + } + } +} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/form.yml b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/form.yml index e828c4d2e..d2a791a4a 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/form.yml +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/form.yml @@ -50,21 +50,19 @@ services: marello_purchase_order.form.purchase_order: class: Symfony\Component\Form\Form factory: ["@form.factory", 'create'] - scope: request arguments: - Marello\Bundle\PurchaseOrderBundle\Form\Type\PurchaseOrderType marello_purchase_order.form.purchase_order_create_step_two: class: Symfony\Component\Form\Form factory: ["@form.factory", 'create'] - scope: request arguments: - Marello\Bundle\PurchaseOrderBundle\Form\Type\PurchaseOrderCreateStepTwoType # Handlers marello_purchase_order.form.handler.purchase_order_create: class: Marello\Bundle\PurchaseOrderBundle\Form\Handler\PurchaseOrderCreateHandler - scope: request + public: true arguments: - "@marello_purchase_order.form.purchase_order_create_step_two" - "@request_stack" @@ -73,7 +71,7 @@ services: marello_purchase_order.form.handler.purchase_order_update: class: Marello\Bundle\PurchaseOrderBundle\Form\Handler\PurchaseOrderUpdateHandler - scope: request + public: true arguments: - '@marello_purchase_order.form.purchase_order' - '@request_stack' diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/jsmodules.yml new file mode 100644 index 000000000..4811bbbda --- /dev/null +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/jsmodules.yml @@ -0,0 +1,11 @@ +dynamic-imports: + marellopurchaseorder: + - marellopurchaseorder/js/multiple-entity + - marellopurchaseorder/js/multiple-entity/view + - marellopurchaseorder/js/multiple-entity/model + - marellopurchaseorder/js/multiple-entity/collection + - marellopurchaseorder/js/app/components/purchaseorder-product-component + - marellopurchaseorder/js/app/views/purchaseorder-item-view + - marellopurchaseorder/js/app/views/purchaseorder-items-view + - marellopurchaseorder/js/app/views/purchaseorder-totals-view + - marellopurchaseorder/js/datagrid/marello-purchase-order-item-candidates-builder diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/oro/datagrids.yml index 09e04649c..6b5e264b1 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/oro/datagrids.yml @@ -51,7 +51,7 @@ datagrids: orderTotal: data_name: po.orderTotal default: - purchaseOrderNumber: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC" + purchaseOrderNumber: "DESC" filters: columns: purchaseOrderNumber: @@ -107,8 +107,11 @@ datagrids: order_id: order_id columns: productSku: - label: marello.product.sku.label - frontend_type: string + label: marello.product.sku.label + data_name: productSku + type: twig + frontend_type: html + template: MarelloProductBundle:Product/Datagrid:productSku.html.twig productName: label: marello.product.name.label frontend_type: string @@ -135,7 +138,7 @@ datagrids: select: - p.id - p.sku - - p.name + - p.denormalizedDefaultName as productName - p.manufacturingCode - sup.id AS preferredSupplierId - sup.name AS preferredSupplier @@ -179,9 +182,10 @@ datagrids: sku: label: marello.product.sku.label frontend_type: string - name: + productName: label: marello.product.name.label frontend_type: string + data_name: productName manufacturingCode: label: marello.product.manufacturing_code.label frontend_type: string @@ -207,7 +211,7 @@ datagrids: columns: hasProduct: { data_name: hasProduct } sku: { data_name: p.sku } - name: { data_name: p.name } + productName: { data_name: p.denormalizedDefaultName } manufacturingCode: { data_name: p.manufacturingCode } preferredSupplier: { data_name: preferredSupplier } purchaseInventory: { data_name: i.purchaseInventory } @@ -215,7 +219,7 @@ datagrids: virtualInventory: { data_name: virtualInventory } orderAmount: { data_name: orderAmount } default: - hasProduct: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + hasProduct: 'DESC' filters: columns: hasProduct: @@ -224,9 +228,9 @@ datagrids: sku: type: string data_name: p.sku - name: + productName: type: string - data_name: p.name + data_name: productName manufacturingCode: type: string data_name: p.manufacturingCode diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/requirejs.yml b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/requirejs.yml deleted file mode 100644 index 9f95c9259..000000000 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/requirejs.yml +++ /dev/null @@ -1,3 +0,0 @@ -config: - paths: - 'marellopurchaseorder/js/app/views/purchaseorder-totals-view': 'bundles/marellopurchaseorder/js/app/views/purchaseorder-totals-view.js' diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/services.yml b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/services.yml index 9d2b409eb..2bfc75fb6 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/config/services.yml @@ -1,6 +1,3 @@ -parameters: - marello_purchaseorder.entity.purchaseorder.class: Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrder - services: marello_purchaseorder.processor.note_activity_processor: class: Marello\Bundle\PurchaseOrderBundle\Processor\NoteActivityProcessor @@ -9,7 +6,7 @@ services: - '@doctrine.orm.entity_manager' marello_purchaseorder.oro_note_entity_service: - class: '%oro_note.entity.class%' + class: 'Oro\Bundle\NoteBundle\Entity\Note' marello_purchaseorder.workflow.receive_purchase_order: class: Marello\Bundle\PurchaseOrderBundle\Workflow\Action\ReceivePurchaseOrderAction diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/components/purchaseorder-product-component.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/components/purchaseorder-product-component.js new file mode 100644 index 000000000..f702d3fbc --- /dev/null +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/components/purchaseorder-product-component.js @@ -0,0 +1,40 @@ +define(function(require) { + 'use strict'; + + const MultipleEntityComponent = require('oroform/js/multiple-entity/component'); + const MultipleEntityModel = require('marellopurchaseorder/js/multiple-entity/model'); + + const PurchaseOrderComponent = MultipleEntityComponent.extend({ + optionNames: MultipleEntityComponent.prototype.optionNames.concat([ + 'currency' + ]), + + /** + * @inheritDoc + */ + constructor: function PurchaseOrderComponent(options) { + PurchaseOrderComponent.__super__.constructor.call(this, options); + }, + + onModelSelect: function(value, model, listener) { + const id = model.get('id'); + if (model.get(listener.columnName)) { + this.addedModels[id] = new MultipleEntityModel({ + 'id': model.get('id'), + 'label': 'product', + 'productName': model.get('productName'), + 'value': model.get('sku') + ' - ' + model.get('productName'), + 'sku': model.get('sku'), + 'orderAmount': model.get('orderAmount'), + 'purchasePrice': model.get('purchasePrice'), + 'currency': this.currency, + isDefault: false + }); + } else if (this.addedModels.hasOwnProperty(id)) { + delete this.addedModels[id]; + } + } + }); + + return PurchaseOrderComponent; +}); \ No newline at end of file diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-item-view.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-item-view.js index 80d5442a1..5fc5fb027 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-item-view.js +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-item-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var PurchaseOrderItemView, + const $ = require('jquery'), _ = require('underscore'), routing = require('routing'), @@ -13,7 +13,7 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemView * @class marellopurchaseorder.app.views.PurchaseOrderItemView */ - PurchaseOrderItemView = AbstractItemView.extend({ + const PurchaseOrderItemView = AbstractItemView.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-items-view.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-items-view.js index 33d60a14b..fbbc48dad 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-items-view.js +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-items-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var PurchaseOrderItemsView, + const $ = require('jquery'), _ = require('underscore'), routing = require('routing'), @@ -13,7 +13,7 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemsView * @class marellopurchaseorder.app.views.PurchaseOrderItemsView */ - PurchaseOrderItemsView = AbstractItemsView.extend({ + const PurchaseOrderItemsView = AbstractItemsView.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-totals-view.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-totals-view.js index 564c16909..b3e046dc7 100755 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-totals-view.js +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/app/views/purchaseorder-totals-view.js @@ -1,19 +1,18 @@ define(function(require) { 'use strict'; - var PurchaseOrderTotalsView; - var template = require('tpl!marellopurchaseorder/templates/purchaseorder/totals.html'); - var $ = require('jquery'); - var _ = require('underscore'); - var mediator = require('oroui/js/mediator'); - var BaseView = require('oroui/js/app/views/base/view'); + const template = require('tpl-loader!marellopurchaseorder/templates/purchaseorder/totals.html'); + const $ = require('jquery'); + const _ = require('underscore'); + const mediator = require('oroui/js/mediator'); + const BaseView = require('oroui/js/app/views/base/view'); /** * @export marellopurchaseorder/js/app/views/purchaseorder-totals-view * @extends oroui.app.views.base.View * @class marellopurchaseorder.app.views.PurchaseOrderTotalsView */ - PurchaseOrderTotalsView = BaseView.extend({ + const PurchaseOrderTotalsView = BaseView.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/datagrid/marello-purchase-order-item-candidates-builder.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/datagrid/marello-purchase-order-item-candidates-builder.js index 7de07a5a5..50c5e7dea 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/datagrid/marello-purchase-order-item-candidates-builder.js +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/datagrid/marello-purchase-order-item-candidates-builder.js @@ -1,17 +1,13 @@ define(function(require) { 'use strict'; - var mediator = require('oroui/js/mediator'); + const mediator = require('oroui/js/mediator'); return { init: function(deferred, options) { - options.gridPromise.done(function(grid) { - - var gridName = grid.name; - + let gridName = grid.name; mediator.trigger('datagrid:doRefresh:' + gridName); - deferred.resolve(); }); } diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity.js index ae6a539bb..837320c8e 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity.js +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity.js @@ -1,15 +1,23 @@ -define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multiple-entity/model', 'oro/dialog-widget', 'oroui/js/mediator' - ], function(_, routing, Backbone, EntityView, MultipleEntityModel, DialogWidget, mediator) { +define(function(require) { 'use strict'; - var $ = Backbone.$; + const _ = require('underscore'); + const routing = require('routing'); + const Backbone = require('backbone'); + const EntityView = require('./multiple-entity/view'); + const DialogWidget = require('oro/dialog-widget'); + const mediator = require('oroui/js/mediator'); + const $ = Backbone.$; /** * @export oroform/js/multiple-entity * @class oroform.MultipleEntity * @extends Backbone.View */ - return Backbone.View.extend({ + const PurchaseOrderMultipleEntityView = Backbone.View.extend({ + template: require('tpl-loader!marellopurchaseorder/js/multiple-entity/templates/multiple-entities.html'), + elementTemplate: require('tpl-loader!marellopurchaseorder/js/multiple-entity/templates/multiple-entity.html'), + options: { addedElement: null, allowAction: true, @@ -31,13 +39,24 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip 'click .add-btn': 'addEntities' }, + /** + * @inheritDoc + */ + constructor: function PurchaseOrderMultipleEntityView(options) { + PurchaseOrderMultipleEntityView.__super__.constructor.call(this, options); + }, + initialize: function(options) { this.options = _.defaults(options || {}, this.options); - this.template = _.template(this.options.template); + if (typeof this.options.template === 'string') { + this.template = _.template(this.options.template); + } + if (typeof this.options.elementTemplate === 'string') { + this.elementTemplate = _.template(this.options.elementTemplate); + } this.listenTo(this.getCollection(), 'add', this.addEntity); this.listenTo(this.getCollection(), 'reset', this._onCollectionReset); this.listenTo(this.getCollection(), 'remove', this.removeDefault); - this.listenTo(this.getCollection(), 'change', this._onCollectionChange); this.$addedEl = $(this.options.addedElement); this.$removedEl = $(this.options.removedElement); @@ -56,16 +75,16 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip }, handleRemove: function(item) { - var itemId = item && item.get('id'); + const itemId = item && item.get('id'); if (!itemId) { return; } - var addedElVal = this.$addedEl.val(); - var removedElVal = this.$removedEl.val(); + const addedElVal = this.$addedEl.val(); + const removedElVal = this.$removedEl.val(); - var added = (addedElVal && addedElVal.split(',')) || []; - var removed = (removedElVal && removedElVal.split(',')) || []; + let added = (addedElVal && addedElVal.split(',')) || []; + const removed = (removedElVal && removedElVal.split(',')) || []; if (_.contains(added, itemId)) { added = _.without(added, itemId); @@ -124,21 +143,21 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip }, _isInitialCollectionItem: function(itemId) { - var isInitial = !!_.find(this.initialCollectionItems, function(id) { + const isInitial = !!_.find(this.initialCollectionItems, function(id) { return String(id) === String(itemId); }); return isInitial; }, _isAddedCollectionItem: function(itemId) { - var isAdded = !!_.find(this.addedCollectionItems, function(id) { + const isAdded = !!_.find(this.addedCollectionItems, function(id) { return String(id) === String(itemId); }); return isAdded; }, _isRemovedCollectionItem: function(itemId) { - var isRemoved = !!_.find(this.removedCollectionItems, function(id) { + const isRemoved = !!_.find(this.removedCollectionItems, function(id) { return String(id) === String(itemId); }); return isRemoved; @@ -159,11 +178,11 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip if (item.get('id') === this.$defaultEl.val()) { item.set('isDefault', true); } - var entityView = new EntityView({ + const entityView = new EntityView({ model: item, name: this.options.name, hasDefault: this.options.defaultElement, - template: this.options.elementTemplate + template: this.elementTemplate }); entityView.on('removal', _.bind(this.handleRemove, this)); this.$entitiesContainer.append(entityView.render().$el); @@ -172,21 +191,21 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip addEntities: function(e) { if (!this.selectorDialog) { - var url = this._getSelectionWidgetUrl(); - var routeAdditionalParams = $(e.target).data('route_additional_params'); + let url = this._getSelectionWidgetUrl(); + const routeAdditionalParams = $(e.target).data('route_additional_params'); if (routeAdditionalParams) { url = url + (url.indexOf('?') === -1 ? '?' : '&') + $.param(routeAdditionalParams); } this.selectorDialog = new DialogWidget({ - url: url, + url: url, title: this.options.selectorWindowTitle, stateEnabled: false, dialogOptions: { - 'modal': true, - 'width': 1024, - 'height': 500, - 'close': _.bind(function() { + modal: true, + width: 1024, + height: 500, + close: _.bind(function() { this.selectorDialog = null; }, this) } @@ -197,21 +216,27 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip }, _getSelectionWidgetUrl: function() { - var url = this.options.selectionUrl || + const url = this.options.selectionUrl || routing.generate(this.options.selectionRouteName, this.options.selectionRouteParams); - var separator = url.indexOf('?') > -1 ? '&' : '?'; - var added = this.$addedEl.val(); - var removed = this.$removedEl.val(); - var defaultEl = this.$defaultEl.val(); + const separator = url.indexOf('?') > -1 ? '&' : '?'; + const added = this.$addedEl.val(); + const removed = this.$removedEl.val(); + const defaultEl = this.$defaultEl.val(); return url + separator + 'added=' + (added || '') + '&removed=' + (removed || '') + - '&default=' + (defaultEl || '') ; + '&default=' + (defaultEl || ''); + }, + + _initWidgets: function() { + _.delay(_.bind(function() { + this.$el.inputWidget('seekAndCreate'); + }, this)); }, processSelectedEntities: function(added, addedModels, removed) { - var self = this; + const self = this; _.intersection(added, removed).forEach(function(itemId) { if (self._isInitialCollectionItem(itemId)) { @@ -239,8 +264,8 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip _.each(addedModels, _.bind(function(model) { this.getCollection().add(model); }, this)); - for (var i = 0; i < removed.length; i++) { - var model = this.getCollection().get(removed[i]); + for (let i = 0; i < removed.length; i++) { + const model = this.getCollection().get(removed[i]); if (model) { model.set('id', null); model.destroy(); @@ -248,15 +273,16 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip } this.selectorDialog.remove(); + + this._initWidgets(); }, triggerTotalsUpdateEvent: function() { - var total = 0; - var currency = null; - var self = this; + let total = 0; + let currency = null; this.getCollection().each(function(model) { if (model.id !== null) { - var rowTotal = parseFloat(model.get('orderAmount')) * parseFloat(model.get('purchasePrice')); + let rowTotal = parseFloat(model.get('orderAmount')) * parseFloat(model.get('purchasePrice')); currency = model.get('currency'); if (!isNaN(rowTotal)) { total = total + rowTotal; @@ -275,7 +301,10 @@ define(['underscore', 'routing', 'backbone', './multiple-entity/view', './multip this.$entitiesContainer = this.$el.find(this.options.entitiesContainerSelector); + this._initWidgets(); return this; } }); + + return PurchaseOrderMultipleEntityView; }); diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/model.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/model.js index 29e7042e4..f7d876346 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/model.js +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/model.js @@ -7,18 +7,26 @@ define(['backbone'], * @class oroform.MultipleEntity.Model * @extends Backbone.Model */ - return Backbone.Model.extend({ + const EntityModel = Backbone.Model.extend({ defaults: { id: null, - link: null, label: null, isDefault: false, sku: null, - name: null, + productName: null, value: null, orderAmount: null, purchasePrice: null, currency: null + }, + + /** + * @inheritDoc + */ + constructor: function EntityModel(attrs, options) { + EntityModel.__super__.constructor.call(this, attrs, options); } + }); - }); + return EntityModel; +}); diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/templates/multiple-entity.html b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/templates/multiple-entity.html index 6c99c84ff..e699b38a8 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/templates/multiple-entity.html +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/templates/multiple-entity.html @@ -28,11 +28,8 @@ - -
-
diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/view.js b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/view.js index a52b47d5b..2c41c6dfb 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/view.js +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/public/js/multiple-entity/view.js @@ -1,16 +1,17 @@ -define(['underscore', 'backbone', 'oro/dialog-widget' - ], function(_, Backbone, DialogWidget) { +define(function(require) { 'use strict'; + const _ = require('underscore'); + const Backbone = require('backbone'); + const DialogWidget = require('oro/dialog-widget'); + /** * @export oroform/js/multiple-entity/view * @class oroform.MultipleEntity.View * @extends Backbone.View */ - return Backbone.View.extend({ - + const EntityView = Backbone.View.extend({ tagName: "tr", - className: "purchase-order-line-item display-values marello-line-item", events: { @@ -26,9 +27,20 @@ define(['underscore', 'backbone', 'oro/dialog-widget' template: null }, + /** + * @inheritDoc + */ + constructor: function EntityView(options) { + EntityView.__super__.constructor.call(this, options); + }, + initialize: function(options) { this.options = _.defaults(options || {}, this.options); - this.template = _.template(this.options.template); + if (typeof this.options.template === 'string') { + this.template = _.template(this.options.template); + } else { + this.template = this.options.template; + } this.listenTo(this.model, 'destroy', this.remove); if (this.options.defaultRequired) { this.listenTo(this.model, 'change:isDefault', this.toggleDefault); @@ -43,7 +55,7 @@ define(['underscore', 'backbone', 'oro/dialog-widget' viewDetails: function(e) { e.stopImmediatePropagation(); e.preventDefault(); - var widget = new DialogWidget({ + let widget = new DialogWidget({ 'url': this.options.model.get('link'), 'title': this.options.model.get('label'), dialogOptions: { @@ -72,8 +84,8 @@ define(['underscore', 'backbone', 'oro/dialog-widget' }, render: function() { - var data = this.model.toJSON(); - data.purchasePrice = parseFloat(data.purchasePrice).toFixed(2) + let data = this.model.toJSON(); + data.purchasePrice = parseFloat(data.purchasePrice).toFixed(2); this.$el.append(this.template(data)); this.$el.find('a.entity-info').click(_.bind(this.viewDetails, this)); this.$el.find('td.purchase-order-line-item-ordered-amount').find('input').change(_.bind(this.updateRowTotal, this)); @@ -84,18 +96,20 @@ define(['underscore', 'backbone', 'oro/dialog-widget' }, updateRowTotal: function() { - var amount = this.$el.find('td.purchase-order-line-item-ordered-amount').find('input').val(); - var price = this.$el.find('td.purchase-order-line-item-purchase-price').find('input[name*="value"]').val(); + let amount = this.$el.find('td.purchase-order-line-item-ordered-amount').find('input').val(); + let price = this.$el.find('td.purchase-order-line-item-purchase-price').find('input[name*="value"]').val(); this.model.set('orderAmount', amount); this.model.set('purchasePrice', price); - - var rowTotal = parseFloat(amount) * parseFloat(price); + + let rowTotal = parseFloat(amount) * parseFloat(price); if (!isNaN(rowTotal)) { - var currencySymbol = this.model.get('currency'); + let currencySymbol = this.model.get('currency'); this.$el.find('td.purchase-order-line-item-row-total').html(currencySymbol + rowTotal.toFixed(2)); } else { this.$el.find('td.purchase-order-line-item-row-total').html(''); } } }); + + return EntityView; }); diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/Form/fields.html.twig index 2a9d66fe5..a7929a4f5 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/Form/fields.html.twig +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/Form/fields.html.twig @@ -78,7 +78,7 @@ {% endblock %} -{% macro marello_purchase_order_item_receive_collection_item_prototype(widget) %} +{% macro marello_purchase_order_item_receive_collection_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -91,7 +91,7 @@ {% endif %} {% if form.vars.value.status != 'complete' and form.vars.value.status != 'closed' %} - {{ form_widget(form) }} @@ -102,7 +102,7 @@ {% endif %} {% endmacro %} -{% macro marello_purchase_order_item_collection_item_prototype(widget) %} +{% macro marello_purchase_order_item_collection_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -115,7 +115,7 @@ {% set allow_delete = true %} {% endif %} - {% endmacro %} -{% macro marello_purchase_order_item_advice_collection_item_prototype(widget) %} +{% macro marello_purchase_order_item_advice_collection_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -150,7 +150,7 @@ {% set allow_delete = true %} {% endif %} - {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_purchase_order_item_collection_item_prototype(child) }} + {{ fields.marello_purchase_order_item_collection_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -216,9 +219,12 @@ {% endblock %} {% block marello_purchase_order_item_advice_collection_widget %} + {% import _self as fields %} + {% spaceless %} + {% set widgetContainerAttributes = block('widget_container_attributes') %} {% if prototype is defined %} - {% set prototype_html = _self.marello_purchase_order_item_advice_collection_item_prototype(form) %} + {% set prototype_html = fields.marello_purchase_order_item_advice_collection_item_prototype(form, widgetContainerAttributes) %} {% endif %} {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} {% set id = id ~ '_collection' %} @@ -242,7 +248,7 @@ {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_purchase_order_item_advice_collection_item_prototype(child) }} + {{ fields.marello_purchase_order_item_advice_collection_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -259,9 +265,12 @@ {% endblock %} {% block marello_purchase_order_item_receive_collection_widget %} + {% import _self as fields %} + {% spaceless %} + {% set widgetContainerAttributes = block('widget_container_attributes') %} {% if prototype is defined %} - {% set prototype_html = _self.marello_purchase_order_item_receive_collection_item_prototype(form) %} + {% set prototype_html = fields.marello_purchase_order_item_receive_collection_item_prototype(form, widgetContainerAttributes) %} {% endif %} {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} {% set id = id ~ '_collection' %} @@ -292,7 +301,7 @@ {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_purchase_order_item_receive_collection_item_prototype(child) }} + {{ fields.marello_purchase_order_item_receive_collection_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -325,41 +334,39 @@
{% endblock %} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/create.html.twig b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/create.html.twig index 00aa65e05..57f607077 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/create.html.twig +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/create.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% set formAction = path('marello_purchaseorder_purchaseorder_create') %} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepOne.html.twig b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepOne.html.twig index b9d5134e9..0fb8c0811 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepOne.html.twig +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepOne.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% set formAction = path('marello_purchaseorder_purchaseorder_create') %} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepTwo.html.twig b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepTwo.html.twig index f02a15714..60064859e 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepTwo.html.twig +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/createStepTwo.html.twig @@ -1,5 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} -{% form_theme form with 'MarelloPurchaseOrderBundle:Form:fields.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% oro_title_set({params : {"%sku%": entity.sku|default('N/A'|trans) , "%name%": (entity.id ? entity.defaultName.string : '')|default('N/A'|trans) } }) %} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/update.html.twig b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/update.html.twig index b7e7c1c1f..79e484efa 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/update.html.twig +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% set formAction = path('marello_purchaseorder_purchaseorder_update', { id: entity.id }) %} diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/view.html.twig b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/view.html.twig index caf1f7338..982b86fc8 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/view.html.twig +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/view.html.twig @@ -6,7 +6,7 @@ {% block navButtons %} {% if marello_purchaseorder_can_edit(entity) %} - {% if resource_granted('EDIT', entity) %} + {% if is_granted('EDIT', entity) %} {{ UI.editButton({ 'path': path('marello_purchaseorder_purchaseorder_update', {'id': entity.id}), 'entity_label': 'marello.purchaseorder.entity_label'|trans diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/widget/productsBySupplier.html.twig b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/widget/productsBySupplier.html.twig index 8ebabc850..f86fafd25 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/widget/productsBySupplier.html.twig +++ b/src/Marello/Bundle/PurchaseOrderBundle/Resources/views/PurchaseOrder/widget/productsBySupplier.html.twig @@ -1,9 +1,10 @@ +{% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} +{% import 'OroUIBundle::macros.html.twig' as UI %} +
- {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% set gridName = 'marello-purchase-order-item-candidates' %} {% set params = {} %} - {% set params = { 'supplierId': supplierId, '_parameters': { @@ -25,58 +26,17 @@ {% endblock %} - +
diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Tests/Unit/EventListener/Doctrine/PurchaseOrderOnOrderOnDemandCreationListenerTest.php b/src/Marello/Bundle/PurchaseOrderBundle/Tests/Unit/EventListener/Doctrine/PurchaseOrderOnOrderOnDemandCreationListenerTest.php index 2d968ecc0..2843e0db0 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Tests/Unit/EventListener/Doctrine/PurchaseOrderOnOrderOnDemandCreationListenerTest.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Tests/Unit/EventListener/Doctrine/PurchaseOrderOnOrderOnDemandCreationListenerTest.php @@ -173,7 +173,7 @@ private function getProduct($id) $inventoryItem = $this->getEntity( InventoryItem::class, ['id' => $id, 'orderOnDemandAllowed' => true], - [null, $product] + [$product] ); $product->addInventoryItem($inventoryItem); /** @var ProductSupplierRelation $productSupplierRelation */ diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Twig/PurchaseOrderExtension.php b/src/Marello/Bundle/PurchaseOrderBundle/Twig/PurchaseOrderExtension.php index 1998ca933..96d46e1df 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Twig/PurchaseOrderExtension.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Twig/PurchaseOrderExtension.php @@ -4,8 +4,10 @@ use Marello\Bundle\PurchaseOrderBundle\Entity\PurchaseOrder; use Oro\Bundle\WorkflowBundle\Model\WorkflowManager; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class PurchaseOrderExtension extends \Twig_Extension +class PurchaseOrderExtension extends AbstractExtension { const NAME = 'marello_purchaseorder'; @@ -40,7 +42,7 @@ public function getName() public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_purchaseorder_can_edit', [$this, 'canEdit'] ) diff --git a/src/Marello/Bundle/PurchaseOrderBundle/Workflow/Action/ReceivePurchaseOrderAction.php b/src/Marello/Bundle/PurchaseOrderBundle/Workflow/Action/ReceivePurchaseOrderAction.php index f9892fa87..b143d4f64 100644 --- a/src/Marello/Bundle/PurchaseOrderBundle/Workflow/Action/ReceivePurchaseOrderAction.php +++ b/src/Marello/Bundle/PurchaseOrderBundle/Workflow/Action/ReceivePurchaseOrderAction.php @@ -13,7 +13,6 @@ use Oro\Component\Action\Action\ActionInterface; use Oro\Component\Action\Exception\InvalidParameterException; use Oro\Component\ConfigExpression\ContextAccessor; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\PropertyAccess\PropertyPathInterface; class ReceivePurchaseOrderAction extends AbstractAction diff --git a/src/Marello/Bundle/RefundBundle/Controller/RefundController.php b/src/Marello/Bundle/RefundBundle/Controller/RefundController.php index 53f216b39..1fe09f0e1 100644 --- a/src/Marello/Bundle/RefundBundle/Controller/RefundController.php +++ b/src/Marello/Bundle/RefundBundle/Controller/RefundController.php @@ -5,17 +5,20 @@ use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\RefundBundle\Entity\Refund; use Marello\Bundle\RefundBundle\Form\Type\RefundType; - -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\HttpFoundation\Request; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Annotation\Route; -class RefundController extends Controller +class RefundController extends AbstractController { /** - * @Config\Route("/", name="marello_refund_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_refund_index" + * ) + * @Template * @AclAncestor("marello_refund_view") */ public function indexAction() @@ -26,8 +29,11 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", name="marello_refund_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * name="marello_refund_view" + * ) + * @Template * @AclAncestor("marello_refund_view") * * @param Refund $entity @@ -40,8 +46,11 @@ public function viewAction(Refund $entity) } /** - * @Config\Route("/create/{id}", name="marello_refund_create") - * @Config\Template("MarelloRefundBundle:Refund:update.html.twig") + * @Route( + * path="/create/{id}", + * name="marello_refund_create" + * ) + * @Template("MarelloRefundBundle:Refund:update.html.twig") * @AclAncestor("marello_refund_create") * * @param Request $request @@ -58,8 +67,12 @@ public function createAction(Request $request, Order $order) /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_refund_update") - * @Config\Template + * @Route( + * path="/update/{id}", + * requirements={"id"="\d+"}, + * name="marello_refund_update" + * ) + * @Template * @AclAncestor("marello_refund_update") * * @param Request $request diff --git a/src/Marello/Bundle/RefundBundle/Entity/Refund.php b/src/Marello/Bundle/RefundBundle/Entity/Refund.php index 5398e4e71..6874d8e19 100644 --- a/src/Marello/Bundle/RefundBundle/Entity/Refund.php +++ b/src/Marello/Bundle/RefundBundle/Entity/Refund.php @@ -7,9 +7,9 @@ use Doctrine\ORM\Mapping as ORM; use Marello\Bundle\CoreBundle\DerivedProperty\DerivedPropertyAwareInterface; use Marello\Bundle\CoreBundle\Model\EntityCreatedUpdatedAtTrait; -use Marello\Bundle\LocaleBundle\Model\LocaleAwareInterface; +use Marello\Bundle\LocaleBundle\Model\LocalizationAwareInterface; use Marello\Bundle\LocaleBundle\Model\LocalizationTrait; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderAwareInterface; use Marello\Bundle\OrderBundle\Entity\OrderItem; @@ -54,7 +54,7 @@ class Refund extends ExtendRefund implements DerivedPropertyAwareInterface, CurrencyAwareInterface, - LocaleAwareInterface, + LocalizationAwareInterface, OrderAwareInterface { use EntityCreatedUpdatedAtTrait; @@ -99,7 +99,7 @@ class Refund extends ExtendRefund implements protected $refundAmount; /** - * @ORM\ManyToOne(targetEntity="Marello\Bundle\OrderBundle\Entity\Customer") + * @ORM\ManyToOne(targetEntity="Marello\Bundle\CustomerBundle\Entity\Customer") * @ORM\JoinColumn(nullable=false) * @Oro\ConfigField( * defaultValues={ @@ -169,7 +169,6 @@ public static function fromOrder(Order $order) ->setCustomer($order->getCustomer()) ->setOrganization($order->getOrganization()) ->setCurrency($order->getCurrency()) - ->setLocale($order->getLocale()) ->setLocalization($order->getLocalization()) ; @@ -196,7 +195,6 @@ public static function fromReturn(ReturnEntity $return) ->setCustomer($return->getOrder()->getCustomer()) ->setOrganization($return->getOrganization()) ->setCurrency($return->getOrder()->getCurrency()) - ->setLocale($return->getOrder()->getLocale()) ->setLocalization($return->getOrder()->getLocalization()) ; diff --git a/src/Marello/Bundle/RefundBundle/Form/Type/OrderItemRefundType.php b/src/Marello/Bundle/RefundBundle/Form/Type/OrderItemRefundType.php index f53453f4d..d00eddf58 100644 --- a/src/Marello/Bundle/RefundBundle/Form/Type/OrderItemRefundType.php +++ b/src/Marello/Bundle/RefundBundle/Form/Type/OrderItemRefundType.php @@ -2,15 +2,13 @@ namespace Marello\Bundle\RefundBundle\Form\Type; +use Marello\Bundle\RefundBundle\Entity\RefundItem; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Form\Extension\Core\Type\MoneyType; -use Symfony\Component\Validator\Constraints\GreaterThan; - -use Marello\Bundle\RefundBundle\Entity\RefundItem; class OrderItemRefundType extends AbstractType { diff --git a/src/Marello/Bundle/RefundBundle/Migrations/Schema/MarelloRefundBundleInstaller.php b/src/Marello/Bundle/RefundBundle/Migrations/Schema/MarelloRefundBundleInstaller.php index 7ce5c5391..b8889a8d0 100644 --- a/src/Marello/Bundle/RefundBundle/Migrations/Schema/MarelloRefundBundleInstaller.php +++ b/src/Marello/Bundle/RefundBundle/Migrations/Schema/MarelloRefundBundleInstaller.php @@ -32,7 +32,7 @@ public function setActivityExtension(ActivityExtension $activityExtension) */ public function getMigrationVersion() { - return 'v1_2'; + return 'v1_3'; } /** @@ -70,7 +70,6 @@ protected function createMarelloRefundTable(Schema $schema) $table->addColumn('created_at', 'datetime', []); $table->addColumn('updated_at', 'datetime', ['notnull' => false]); $table->addColumn('localization_id', 'integer', ['notnull' => false]); - $table->addColumn('locale', 'string', ['notnull' => false, 'length' => 5]); $table->setPrimaryKey(['id']); $table->addUniqueIndex(['refund_number'], 'UNIQ_973FA8836E8C706D'); $table->addIndex(['customer_id'], 'IDX_973FA8839395C3F3', []); @@ -117,7 +116,7 @@ protected function addMarelloRefundForeignKeys(Schema $schema) ['onDelete' => 'SET NULL', 'onUpdate' => null] ); $table->addForeignKeyConstraint( - $schema->getTable('marello_order_customer'), + $schema->getTable('marello_customer_customer'), ['customer_id'], ['id'], ['onDelete' => null, 'onUpdate' => null] diff --git a/src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_1/MarelloRefundBundle.php b/src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_1/MarelloRefundBundle.php index 6beaa84d8..8428b549b 100644 --- a/src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_1/MarelloRefundBundle.php +++ b/src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_1/MarelloRefundBundle.php @@ -3,10 +3,8 @@ namespace Marello\Bundle\RefundBundle\Migrations\Schema\v1_1; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtension; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtensionAwareInterface; -use Oro\Bundle\MigrationBundle\Migration\QueryBag; use Oro\Bundle\MigrationBundle\Migration\Migration; +use Oro\Bundle\MigrationBundle\Migration\QueryBag; /** * @SuppressWarnings(PHPMD.TooManyMethods) diff --git a/src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_3/MarelloRefundBundle.php b/src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_3/MarelloRefundBundle.php new file mode 100644 index 000000000..d8052feab --- /dev/null +++ b/src/Marello/Bundle/RefundBundle/Migrations/Schema/v1_3/MarelloRefundBundle.php @@ -0,0 +1,44 @@ +getTable('marello_refund'); + $table->dropColumn('locale'); + if ($table->hasForeignKey('fk_marello_refund_customer_id')) { + $table->removeForeignKey('fk_marello_refund_customer_id'); + } + $table->addForeignKeyConstraint( + $schema->getTable('marello_customer_customer'), + ['customer_id'], + ['id'], + ['onDelete' => null, 'onUpdate' => null] + ); + + $dropLocaleInConfigSql = <<addSql( + $dropLocaleInConfigSql, + ['field_name' => 'locale', 'class_name' => Refund::class], + ['field_name' => Type::STRING, 'class_name' => Type::STRING] + ); + $queries->addPostQuery($dropLocaleInConfigQuery); + } +} diff --git a/src/Marello/Bundle/RefundBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/RefundBundle/Resources/config/oro/datagrids.yml index c66096440..9a164f784 100644 --- a/src/Marello/Bundle/RefundBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/RefundBundle/Resources/config/oro/datagrids.yml @@ -15,7 +15,7 @@ datagrids: label: marello.refund.refund_number.label frontend_type: string customer: - label: marello.order.customer.entity_label + label: marello.customer.entity_label frontend_type: string orderNumber: data_name: order.orderNumber @@ -52,7 +52,7 @@ datagrids: updatedAt: data_name: r.updatedAt default: - refundNumber: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC" + refundNumber: "DESC" filters: columns: refundNumber: @@ -155,7 +155,7 @@ datagrids: updatedAt: data_name: r.updatedAt default: - refundNumber: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC" + refundNumber: "DESC" filters: columns: refundNumber: diff --git a/src/Marello/Bundle/RefundBundle/Resources/config/services.yml b/src/Marello/Bundle/RefundBundle/Resources/config/services.yml index bbe516ef0..e25428471 100644 --- a/src/Marello/Bundle/RefundBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/RefundBundle/Resources/config/services.yml @@ -11,8 +11,7 @@ services: class: Marello\Bundle\RefundBundle\Twig\RefundExtension arguments: - '@oro_workflow.manager' - calls: - - ['setRefundBalanceCalculator', ['@marello_refund.calculator.refund_balance']] + - '@marello_refund.calculator.refund_balance' tags: - { name: twig.extension } @@ -32,6 +31,7 @@ services: marello_refund.datagrid.action_permission_provider: class: 'Marello\Bundle\RefundBundle\Datagrid\RefundActionPermissionProvider' + public: true arguments: - '@doctrine.orm.entity_manager' diff --git a/src/Marello/Bundle/RefundBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/RefundBundle/Resources/translations/messages.en.yml index 7c6202661..e898a3355 100644 --- a/src/Marello/Bundle/RefundBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/RefundBundle/Resources/translations/messages.en.yml @@ -11,7 +11,6 @@ marello: additional_items.label: Additional Items original_order.label: Original Order currency.label: Currency - locale.label: Locale customer.label: Customer items.label: Items localization.label: Localization @@ -24,11 +23,11 @@ marello: base_amount.label: Base Amount id.label: Id name.label: Name - quantity.label: Quantity - refund_amount.label: Refund Amount - quantity_ordered.label: Ordered Quantity order_item.label: Order Item refund.label: Refund + quantity_ordered.label: Ordered Quantity + quantity.label: Refunded Quantity + refund_amount.label: Refunded Amount organization.label: Organization form: price.label: Price (incl VAT) diff --git a/src/Marello/Bundle/RefundBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/RefundBundle/Resources/views/Form/fields.html.twig index 30d2f3e0e..d24c66cde 100644 --- a/src/Marello/Bundle/RefundBundle/Resources/views/Form/fields.html.twig +++ b/src/Marello/Bundle/RefundBundle/Resources/views/Form/fields.html.twig @@ -53,7 +53,7 @@ {% endblock %} -{% macro marello_order_item_refund_prototype(widget) %} +{% macro marello_order_item_refund_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -72,13 +72,13 @@ {% set page_component_options = { 'disabled': not allow_delete } %} - {{ form_widget(form) }} {% endmacro %} -{% macro marello_additional_refund_prototype(widget) %} +{% macro marello_additional_refund_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -97,7 +97,7 @@ {% set page_component_options = { 'disabled': not allow_delete } %} - {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_order_item_refund_prototype(child) }} + {{ fields.marello_order_item_refund_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} @@ -157,9 +160,12 @@ {% endblock %} {% block marello_additional_refund_collection_widget %} + {% import _self as fields %} + {% spaceless %} + {% set widgetContainerAttributes = block('widget_container_attributes') %} {% if prototype is defined %} - {% set prototype_html = _self.marello_additional_refund_prototype(form) %} + {% set prototype_html = fields.marello_additional_refund_prototype(form, widgetContainerAttributes) %} {% endif %} {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} {% set id = id ~ '_collection' %} @@ -180,7 +186,7 @@ {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_additional_refund_prototype(child) }} + {{ fields.marello_additional_refund_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} diff --git a/src/Marello/Bundle/RefundBundle/Resources/views/Refund/create.html.twig b/src/Marello/Bundle/RefundBundle/Resources/views/Refund/create.html.twig index de2f9c50b..5a109ffb1 100644 --- a/src/Marello/Bundle/RefundBundle/Resources/views/Refund/create.html.twig +++ b/src/Marello/Bundle/RefundBundle/Resources/views/Refund/create.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% set formAction = path('marello_refund_create') %} @@ -6,7 +7,7 @@ {% block navButtons %} {{ UI.cancelButton(path('marello_refund_index')) }} {% set html = UI.saveAndCloseButton() %} - {% if resource_granted('inventory.vehicle_update') %} + {% if is_granted('inventory.vehicle_update') %} {% set html = html ~ UI.saveAndStayButton() %} {% endif %} {{ UI.dropdownSaveButton({ 'html': html }) }} diff --git a/src/Marello/Bundle/RefundBundle/Resources/views/Refund/update.html.twig b/src/Marello/Bundle/RefundBundle/Resources/views/Refund/update.html.twig index 1246395ce..41c656777 100644 --- a/src/Marello/Bundle/RefundBundle/Resources/views/Refund/update.html.twig +++ b/src/Marello/Bundle/RefundBundle/Resources/views/Refund/update.html.twig @@ -11,7 +11,7 @@ {% block navButtons %} {{ UI.cancelButton(path('marello_refund_index')) }} {% set html = UI.saveAndCloseButton() %} - {% if resource_granted('marello_refund_update') %} + {% if is_granted('marello_refund_update') %} {% set html = html ~ UI.saveAndStayButton() %} {% endif %} {{ UI.dropdownSaveButton({ 'html': html }) }} diff --git a/src/Marello/Bundle/RefundBundle/Resources/views/Refund/view.html.twig b/src/Marello/Bundle/RefundBundle/Resources/views/Refund/view.html.twig index da8f0a8ef..b5145d7d2 100644 --- a/src/Marello/Bundle/RefundBundle/Resources/views/Refund/view.html.twig +++ b/src/Marello/Bundle/RefundBundle/Resources/views/Refund/view.html.twig @@ -3,7 +3,7 @@ {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% block navButtons %} - {% if resource_granted('marello_refund_update') and marello_refund_is_pending(entity) %} + {% if is_granted('marello_refund_update') and marello_refund_is_pending(entity) %} {{ UI.editButton({ 'path' : path('marello_refund_update', { id: entity.id }), 'entity_label': 'marello.refund.entity_label'|trans diff --git a/src/Marello/Bundle/RefundBundle/Tests/Functional/DataFixtures/LoadRefundData.php b/src/Marello/Bundle/RefundBundle/Tests/Functional/DataFixtures/LoadRefundData.php index bb08c550b..321e51eae 100644 --- a/src/Marello/Bundle/RefundBundle/Tests/Functional/DataFixtures/LoadRefundData.php +++ b/src/Marello/Bundle/RefundBundle/Tests/Functional/DataFixtures/LoadRefundData.php @@ -5,12 +5,10 @@ use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; - -use Marello\Bundle\OrderBundle\Entity\Order; -use Marello\Bundle\RefundBundle\Entity\Refund; use Marello\Bundle\OrderBundle\Entity\OrderItem; -use Marello\Bundle\RefundBundle\Entity\RefundItem; use Marello\Bundle\OrderBundle\Tests\Functional\DataFixtures\LoadOrderData; +use Marello\Bundle\RefundBundle\Entity\Refund; +use Marello\Bundle\RefundBundle\Entity\RefundItem; class LoadRefundData extends AbstractFixture implements DependentFixtureInterface { diff --git a/src/Marello/Bundle/RefundBundle/Twig/RefundExtension.php b/src/Marello/Bundle/RefundBundle/Twig/RefundExtension.php index 00946a723..06d97af88 100644 --- a/src/Marello/Bundle/RefundBundle/Twig/RefundExtension.php +++ b/src/Marello/Bundle/RefundBundle/Twig/RefundExtension.php @@ -5,8 +5,10 @@ use Marello\Bundle\RefundBundle\Calculator\RefundBalanceCalculator; use Marello\Bundle\RefundBundle\Entity\Refund; use Oro\Bundle\WorkflowBundle\Model\WorkflowManager; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class RefundExtension extends \Twig_Extension +class RefundExtension extends AbstractExtension { const NAME = 'marello_refund'; @@ -21,11 +23,16 @@ class RefundExtension extends \Twig_Extension protected $refundBalanceCalculator; /** + * RefundExtension constructor. * @param WorkflowManager $workflowManager + * @param RefundBalanceCalculator $balanceCalculator */ - public function __construct(WorkflowManager $workflowManager) - { + public function __construct( + WorkflowManager $workflowManager, + RefundBalanceCalculator $balanceCalculator + ) { $this->workflowManager = $workflowManager; + $this->refundBalanceCalculator = $balanceCalculator; } /** @@ -46,11 +53,11 @@ public function getName() public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_refund_is_pending', [$this, 'isPending'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_refund_get_balance', [$this, 'getBalance'] ), @@ -81,18 +88,4 @@ public function getBalance(Refund $refund) { return $this->refundBalanceCalculator->caclulateBalance($refund); } - - /** - * Add refund calculator via call method on service in order to keep BC - * should be removed for 3.0 - * @deprecated remove in 3.0 - * @param RefundBalanceCalculator $balanceCalculator - * @return $this - */ - public function setRefundBalanceCalculator(RefundBalanceCalculator $balanceCalculator) - { - $this->refundBalanceCalculator = $balanceCalculator; - - return $this; - } } diff --git a/src/Marello/Bundle/ReportBundle/Controller/ReportController.php b/src/Marello/Bundle/ReportBundle/Controller/ReportController.php index a282b43a5..3e16950ae 100644 --- a/src/Marello/Bundle/ReportBundle/Controller/ReportController.php +++ b/src/Marello/Bundle/ReportBundle/Controller/ReportController.php @@ -2,21 +2,21 @@ namespace Marello\Bundle\ReportBundle\Controller; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Routing\Annotation\Route; -class ReportController extends Controller +class ReportController extends AbstractController { /** - * @Config\Route( - * "/static/{reportGroupName}/{reportName}/{_format}", + * @Route( + * path="/static/{reportGroupName}/{reportName}/{_format}", * name="marello_report_index", * requirements={"reportGroupName"="\w+", "reportName"="\w+", "_format"="html|json"}, * defaults={"_format" = "html"} * ) - * @Config\Template + * @Template * @AclAncestor("oro_report_view") * * @param string $reportGroupName diff --git a/src/Marello/Bundle/ReportBundle/DependencyInjection/Configuration.php b/src/Marello/Bundle/ReportBundle/DependencyInjection/Configuration.php index a7aec9923..e9878ed52 100644 --- a/src/Marello/Bundle/ReportBundle/DependencyInjection/Configuration.php +++ b/src/Marello/Bundle/ReportBundle/DependencyInjection/Configuration.php @@ -13,7 +13,7 @@ class Configuration implements ConfigurationInterface public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('marello_report'); + $treeBuilder->root('marello_report'); // Here you should define the parameters that are allowed to // configure your bundle. See the documentation linked above for diff --git a/src/Marello/Bundle/ReportBundle/DependencyInjection/MarelloReportExtension.php b/src/Marello/Bundle/ReportBundle/DependencyInjection/MarelloReportExtension.php index 5f0d5f36b..dde8f52df 100644 --- a/src/Marello/Bundle/ReportBundle/DependencyInjection/MarelloReportExtension.php +++ b/src/Marello/Bundle/ReportBundle/DependencyInjection/MarelloReportExtension.php @@ -20,7 +20,7 @@ class MarelloReportExtension extends Extension public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); + $this->processConfiguration($configuration, $configs); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); diff --git a/src/Marello/Bundle/ReportBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/ReportBundle/Resources/config/oro/datagrids.yml index f4f889062..680f51a55 100644 --- a/src/Marello/Bundle/ReportBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/ReportBundle/Resources/config/oro/datagrids.yml @@ -36,7 +36,7 @@ datagrids: totalOrders: { data_name: totalOrders } totalRevenue: { data_name: totalRevenue } default: - totalRevenue: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + totalRevenue: 'DESC' options: entityHint: report data export: false @@ -49,7 +49,7 @@ datagrids: query: select: - i.id - - p.name as productName + - p.denormalizedDefaultName as productName - p.sku as productSku - s.label as status - COALESCE(SUM(il.inventory), 0) AS inventoryQty @@ -66,7 +66,7 @@ datagrids: label: marello.product.sku.label frontend_type: string productName: - label: marello.product.name.label + label: marello.product.names.label frontend_type: string inventoryQty: label: marello.inventory.inventorylevel.inventory.label @@ -78,11 +78,11 @@ datagrids: sorters: columns: productSku: { data_name: p.sku } - productName: { data_name: p.name } + productName: { data_name: p.denormalizedDefaultName } inventoryQty: { data_name: inventoryQty } status: { data_name: s.label } default: - inventoryQty: %oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC + inventoryQty: 'ASC' options: entityHint: report data export: false @@ -125,7 +125,7 @@ datagrids: createdAt: { data_name: createdAt } quantitySold: { data_name: quantitySold } default: - quantitySold: %oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC + quantitySold: 'DESC' options: entityHint: report data export: false @@ -173,7 +173,7 @@ datagrids: createdAt: { data_name: createdAt } quantitySold: { data_name: quantitySold } default: - quantitySold: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC' + quantitySold: 'ASC' options: entityHint: report data export: false @@ -222,7 +222,7 @@ datagrids: returnReason: { data_name: returnReason } percentageReturned: { data_name: percentageReturned } default: - productSku: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC' + productSku: 'ASC' filters: columns: productSku: @@ -286,7 +286,7 @@ datagrids: quantityOrdered: { data_name: quantityOrdered } percentageReturned: { data_name: percentageReturned } default: - productSku: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC' + productSku: 'ASC' filters: columns: productSku: diff --git a/src/Marello/Bundle/ReportBundle/Resources/config/services.yml b/src/Marello/Bundle/ReportBundle/Resources/config/services.yml index 3a4b8c013..b3bcc6ff4 100644 --- a/src/Marello/Bundle/ReportBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/ReportBundle/Resources/config/services.yml @@ -1,6 +1,6 @@ services: marello_report.listener.datagrid.workflow_grid_listener: - class: '%marello_datagrid.event_listener.datagrid.workflow_grid_listener.class%' + class: 'Marello\Bundle\DataGridBundle\EventListener\Datagrid\WorkflowGridListener' tags: - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.after.marello_report-orders-revenue_per_sales_channel, method: removeWorkflow } diff --git a/src/Marello/Bundle/ReturnBundle/Controller/ReturnController.php b/src/Marello/Bundle/ReturnBundle/Controller/ReturnController.php index ba9dfef10..79e558534 100644 --- a/src/Marello/Bundle/ReturnBundle/Controller/ReturnController.php +++ b/src/Marello/Bundle/ReturnBundle/Controller/ReturnController.php @@ -7,16 +7,20 @@ use Marello\Bundle\ReturnBundle\Form\Type\ReturnType; use Marello\Bundle\ReturnBundle\Form\Type\ReturnUpdateType; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Oro\Bundle\SecurityBundle\Exception\ForbiddenException; +use Symfony\Component\Routing\Annotation\Route; -class ReturnController extends Controller +class ReturnController extends AbstractController { /** - * @Config\Route("/", name="marello_return_return_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_return_return_index" + * ) + * @Template * @AclAncestor("marello_return_view") */ public function indexAction() @@ -25,8 +29,12 @@ public function indexAction() } /** - * @Config\Route("/create/{id}", requirements={"id"="\d+"}, name="marello_return_return_create") - * @Config\Template + * @Route( + * path="/create/{id}", + * requirements={"id"="\d+"}, + * name="marello_return_return_create" + * ) + * @Template * @AclAncestor("marello_return_create") * * @param Order $order @@ -81,8 +89,12 @@ public function createAction(Order $order, Request $request) } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_return_return_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_return_return_view" + * ) + * @Template * @AclAncestor("marello_return_view") * * @param ReturnEntity $return @@ -95,8 +107,12 @@ public function viewAction(ReturnEntity $return) } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_return_return_update") - * @Config\Template + * @Route( + * path="/update/{id}", + * requirements={"id"="\d+"}, + * name="marello_return_return_update" + * ) + * @Template * @AclAncestor("marello_return_update") * * @param ReturnEntity $return diff --git a/src/Marello/Bundle/ReturnBundle/Entity/Repository/ReturnItemRepository.php b/src/Marello/Bundle/ReturnBundle/Entity/Repository/ReturnItemRepository.php index c3edc64bc..d97e505ed 100644 --- a/src/Marello/Bundle/ReturnBundle/Entity/Repository/ReturnItemRepository.php +++ b/src/Marello/Bundle/ReturnBundle/Entity/Repository/ReturnItemRepository.php @@ -3,10 +3,6 @@ namespace Marello\Bundle\ReturnBundle\Entity\Repository; use Doctrine\ORM\EntityRepository; -use Doctrine\ORM\Query\Expr\Join; -use Doctrine\ORM\Query\ResultSetMapping; -use Marello\Bundle\OrderBundle\Entity\OrderItem; -use Marello\Bundle\ReturnBundle\Entity\ReturnItem; class ReturnItemRepository extends EntityRepository { diff --git a/src/Marello/Bundle/ReturnBundle/Entity/ReturnEntity.php b/src/Marello/Bundle/ReturnBundle/Entity/ReturnEntity.php index 294f57ddc..a172558f7 100644 --- a/src/Marello/Bundle/ReturnBundle/Entity/ReturnEntity.php +++ b/src/Marello/Bundle/ReturnBundle/Entity/ReturnEntity.php @@ -6,7 +6,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Marello\Bundle\CoreBundle\DerivedProperty\DerivedPropertyAwareInterface; -use Marello\Bundle\LocaleBundle\Model\LocaleAwareInterface; +use Marello\Bundle\LocaleBundle\Model\LocalizationAwareInterface; use Marello\Bundle\LocaleBundle\Model\LocalizationTrait; use Marello\Bundle\CoreBundle\Model\EntityCreatedUpdatedAtTrait; use Marello\Bundle\OrderBundle\Entity\Order; @@ -14,6 +14,7 @@ use Marello\Bundle\ReturnBundle\Model\ExtendReturnEntity; use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Marello\Bundle\ShippingBundle\Entity\HasShipmentTrait; +use Marello\Bundle\SalesBundle\Model\SalesChannelAwareInterface; use Marello\Bundle\ShippingBundle\Integration\ShippingAwareInterface; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; use Oro\Bundle\OrganizationBundle\Entity\OrganizationAwareInterface; @@ -52,9 +53,10 @@ class ReturnEntity extends ExtendReturnEntity implements DerivedPropertyAwareInterface, ShippingAwareInterface, - LocaleAwareInterface, + LocalizationAwareInterface, OrganizationAwareInterface, - OrderAwareInterface + OrderAwareInterface, + SalesChannelAwareInterface { use HasShipmentTrait; use LocalizationTrait; @@ -197,7 +199,6 @@ public function setOrder(Order $order) { $this->order = $order; $this->organization = $order->getOrganization(); - $this->locale= $order->getLocale(); $this->localization = $order->getLocalization(); return $this; diff --git a/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnItemTypeSubscriber.php b/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnItemTypeSubscriber.php index 174045c21..684fb591e 100644 --- a/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnItemTypeSubscriber.php +++ b/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnItemTypeSubscriber.php @@ -3,8 +3,6 @@ namespace Marello\Bundle\ReturnBundle\Form\EventListener; use Doctrine\ORM\EntityManager; -use Marello\Bundle\OrderBundle\Entity\OrderItem; -use Marello\Bundle\ReturnBundle\Entity\ReturnEntity; use Marello\Bundle\ReturnBundle\Entity\ReturnItem; use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; use Symfony\Component\EventDispatcher\EventSubscriberInterface; diff --git a/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnTypeSubscriber.php b/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnTypeSubscriber.php index 916b6d3aa..e2f7fa6e9 100644 --- a/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnTypeSubscriber.php +++ b/src/Marello/Bundle/ReturnBundle/Form/EventListener/ReturnTypeSubscriber.php @@ -35,7 +35,8 @@ public function onPreSetData(FormEvent $event) ->getItems() ->map(function (OrderItem $orderItem) use ($return) { $status = $orderItem->getStatus(); - if (in_array($status, [LoadOrderItemStatusData::DROPSHIPPING, LoadOrderItemStatusData::SHIPPED])) { + $statuses = [LoadOrderItemStatusData::DROPSHIPPING, LoadOrderItemStatusData::SHIPPED]; + if (in_array($status->getId(), $statuses)) { $return->addReturnItem(new ReturnItem($orderItem)); } }); diff --git a/src/Marello/Bundle/ReturnBundle/Manager/BusinessRuleManager.php b/src/Marello/Bundle/ReturnBundle/Manager/BusinessRuleManager.php index de7e2c8cc..bb88a703d 100644 --- a/src/Marello/Bundle/ReturnBundle/Manager/BusinessRuleManager.php +++ b/src/Marello/Bundle/ReturnBundle/Manager/BusinessRuleManager.php @@ -2,9 +2,6 @@ namespace Marello\Bundle\ReturnBundle\Manager; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Doctrine\Common\Collections\ArrayCollection; - class BusinessRuleManager { /** @var ReturnBusinessRuleRegistry $businessRuleregistry */ diff --git a/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloProductWarranty.php b/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloProductWarranty.php index 80a14273c..7c33e1dbc 100644 --- a/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloProductWarranty.php +++ b/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloProductWarranty.php @@ -60,6 +60,8 @@ public function applyRule($entity) $this->updateStatus(self::RETURN_ITEM_STATUS_AUTHORIZED, $returnItem); } }); + + return $this; } /** diff --git a/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloRorWarranty.php b/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloRorWarranty.php index e24e4343a..4ed12e210 100644 --- a/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloRorWarranty.php +++ b/src/Marello/Bundle/ReturnBundle/Manager/Rules/MarelloRorWarranty.php @@ -60,6 +60,8 @@ public function applyRule($entity) $this->updateStatus(self::RETURN_ITEM_STATUS_AUTHORIZED, $returnItem); } }); + + return $this; } /** diff --git a/src/Marello/Bundle/ReturnBundle/Migrations/Schema/MarelloReturnBundleInstaller.php b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/MarelloReturnBundleInstaller.php index c3c56ecfd..908635b4e 100644 --- a/src/Marello/Bundle/ReturnBundle/Migrations/Schema/MarelloReturnBundleInstaller.php +++ b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/MarelloReturnBundleInstaller.php @@ -31,7 +31,7 @@ class MarelloReturnBundleInstaller implements */ public function getMigrationVersion() { - return 'v1_3'; + return 'v1_4'; } /** @@ -113,7 +113,6 @@ protected function createMarelloReturnReturnTable(Schema $schema) $table->addColumn('updated_at', 'datetime', ['notnull' => false]); $table->addColumn('sales_channel_name', 'string', ['length' => 255]); $table->addColumn('sales_channel_id', 'integer', ['notnull' => false]); - $table->addColumn('locale', 'string', ['notnull' => false, 'length' => 5]); $table->addColumn('localization_id', 'integer', ['notnull' => false]); $table->addColumn('shipment_id', 'integer', ['notnull' => false]); $table->addColumn('return_reference', 'string', ['notnull' => false, 'length' => 255]); diff --git a/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_1/MarelloReturnBundle.php b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_1/MarelloReturnBundle.php index 6af0bea44..06cbe8f3d 100644 --- a/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_1/MarelloReturnBundle.php +++ b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_1/MarelloReturnBundle.php @@ -3,13 +3,8 @@ namespace Marello\Bundle\ReturnBundle\Migrations\Schema\v1_1; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope; -use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtension; -use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtensionAwareInterface; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtension; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtensionAwareInterface; /** * @SuppressWarnings(PHPMD.TooManyMethods) diff --git a/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_2/MarelloReturnBundle.php b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_2/MarelloReturnBundle.php index f62578008..8c0fedced 100644 --- a/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_2/MarelloReturnBundle.php +++ b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_2/MarelloReturnBundle.php @@ -3,13 +3,8 @@ namespace Marello\Bundle\ReturnBundle\Migrations\Schema\v1_2; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope; -use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtension; -use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtensionAwareInterface; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtension; -use Oro\Bundle\ActivityBundle\Migration\Extension\ActivityExtensionAwareInterface; /** * @SuppressWarnings(PHPMD.TooManyMethods) diff --git a/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_4/MarelloReturnBundle.php b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_4/MarelloReturnBundle.php new file mode 100644 index 000000000..fbbe38756 --- /dev/null +++ b/src/Marello/Bundle/ReturnBundle/Migrations/Schema/v1_4/MarelloReturnBundle.php @@ -0,0 +1,35 @@ +getTable('marello_return_return'); + $table->dropColumn('locale'); + + $dropLocaleInConfigSql = <<addSql( + $dropLocaleInConfigSql, + ['field_name' => 'locale', 'class_name' => ReturnEntity::class], + ['field_name' => Type::STRING, 'class_name' => Type::STRING] + ); + $queries->addPostQuery($dropLocaleInConfigQuery); + } +} diff --git a/src/Marello/Bundle/ReturnBundle/Resources/config/oro/api.yml b/src/Marello/Bundle/ReturnBundle/Resources/config/oro/api.yml index e44efd173..b97b7c467 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/config/oro/api.yml +++ b/src/Marello/Bundle/ReturnBundle/Resources/config/oro/api.yml @@ -30,8 +30,6 @@ api: exclude: true salesChannelName: exclude: true - locale: - exclude: true localization: exclude: true update: false diff --git a/src/Marello/Bundle/ReturnBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/ReturnBundle/Resources/config/oro/datagrids.yml index 86405de62..831b1f6e8 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/ReturnBundle/Resources/config/oro/datagrids.yml @@ -69,7 +69,7 @@ datagrids: updatedAt: data_name: r.updatedAt default: - returnNumber: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + returnNumber: 'DESC' properties: view_link: type: url @@ -161,7 +161,7 @@ datagrids: updatedAt: data_name: r.updatedAt default: - returnNumber: '%oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC' + returnNumber: 'DESC' properties: view_link: type: url diff --git a/src/Marello/Bundle/ReturnBundle/Resources/config/services.yml b/src/Marello/Bundle/ReturnBundle/Resources/config/services.yml index 218c2b62f..3606e8e56 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/ReturnBundle/Resources/config/services.yml @@ -1,6 +1,3 @@ -parameters: - marello_return.entity.return.class: Marello\Bundle\ReturnBundle\Entity\ReturnEntity - services: marello_return.util.return_helper: class: Marello\Bundle\ReturnBundle\Util\ReturnHelper @@ -31,6 +28,7 @@ services: - '@service_container' marello_return.manager.businessrule_manager: + public: true class: Marello\Bundle\ReturnBundle\Manager\BusinessRuleManager arguments: - '@marello_return.manager.return_businessrule_registry' diff --git a/src/Marello/Bundle/ReturnBundle/Resources/config/shipping.yml b/src/Marello/Bundle/ReturnBundle/Resources/config/shipping.yml index fb4b003d1..19bb6de0c 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/config/shipping.yml +++ b/src/Marello/Bundle/ReturnBundle/Resources/config/shipping.yml @@ -4,4 +4,4 @@ services: arguments: - '@doctrine.orm.entity_manager' tags: - - { name: marello.shipping.data_provider, class: '%marello_return.entity.return.class%' } + - { name: marello.shipping.data_provider, class: 'Marello\Bundle\ReturnBundle\Entity\ReturnEntity' } diff --git a/src/Marello/Bundle/ReturnBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/ReturnBundle/Resources/translations/messages.en.yml index 4e98e2359..5d54365e5 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/ReturnBundle/Resources/translations/messages.en.yml @@ -10,7 +10,6 @@ marello: return_items.label: Returned items workflow_item.label: Workflow Item workflow_step.label: Workflow Step - locale.label: Locale sales_channel_name.label: Sales Channel Name localization.label: Localization organization.label: Organization diff --git a/src/Marello/Bundle/ReturnBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/ReturnBundle/Resources/views/Form/fields.html.twig index 23dd1a798..a65253df5 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/views/Form/fields.html.twig +++ b/src/Marello/Bundle/ReturnBundle/Resources/views/Form/fields.html.twig @@ -40,7 +40,7 @@ {% endblock %} -{% macro marello_return_item_collection_item_prototype(widget) %} +{% macro marello_return_item_collection_item_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -52,7 +52,7 @@ {% set disabled = widget.parent.vars.disabled %} {% endif %} - {{ form_widget(form) }} @@ -63,9 +63,12 @@ {% endmacro %} {% block marello_return_item_collection_widget %} + {% import _self as fields %} + {% spaceless %} + {% set widgetContainerAttributes = block('widget_container_attributes') %} {% if prototype is defined %} - {% set prototype_html = _self.marello_return_item_collection_item_prototype(form) %} + {% set prototype_html = fields.marello_return_item_collection_item_prototype(form, widgetContainerAttributes) %} {% endif %} {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} {% set id = id ~ '_collection' %} @@ -88,7 +91,7 @@ {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_return_item_collection_item_prototype(child) }} + {{ fields.marello_return_item_collection_item_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {{ prototype_html|replace({(prototype_name): '0'})|raw }} diff --git a/src/Marello/Bundle/ReturnBundle/Resources/views/Return/create.html.twig b/src/Marello/Bundle/ReturnBundle/Resources/views/Return/create.html.twig index 773317c62..57b599a00 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/views/Return/create.html.twig +++ b/src/Marello/Bundle/ReturnBundle/Resources/views/Return/create.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% set order = form.vars.value.order %} diff --git a/src/Marello/Bundle/ReturnBundle/Resources/views/Return/update.html.twig b/src/Marello/Bundle/ReturnBundle/Resources/views/Return/update.html.twig index 88294492e..1d709cb9e 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/views/Return/update.html.twig +++ b/src/Marello/Bundle/ReturnBundle/Resources/views/Return/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% oro_title_set({params : {"%id%": form.vars.value.id }}) %} @@ -10,7 +11,7 @@ {{ UI.cancelButton(path('marello_return_return_view', {id: form.vars.value.id })) }} {% set html = UI.saveAndCloseButton() %} - {% if form.vars.value.id or resource_granted('marello_return_update') %} + {% if form.vars.value.id or is_granted('marello_return_update') %} {% set html = html ~ UI.saveAndStayButton() %} {% endif %} {{ UI.dropdownSaveButton({'html': html}) }} diff --git a/src/Marello/Bundle/ReturnBundle/Resources/views/Return/view.html.twig b/src/Marello/Bundle/ReturnBundle/Resources/views/Return/view.html.twig index a51143651..f55fdf651 100644 --- a/src/Marello/Bundle/ReturnBundle/Resources/views/Return/view.html.twig +++ b/src/Marello/Bundle/ReturnBundle/Resources/views/Return/view.html.twig @@ -16,7 +16,7 @@ {% endblock pageHeader %} {% block navButtons %} - {% if resource_granted('EDIT', entity) and marello_return_is_on_hold(entity) %} + {% if is_granted('EDIT', entity) and marello_return_is_on_hold(entity) %} {{ UI.buttonSeparator() }} {{ UI.editButton({ 'path': path('marello_return_return_update', {'id': entity.id}), diff --git a/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/cget_return_list.yml b/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/cget_return_list.yml index 3edd104a8..219e5ad32 100644 --- a/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/cget_return_list.yml +++ b/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/cget_return_list.yml @@ -6,7 +6,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return0->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -49,7 +48,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return1->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -89,7 +87,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return2->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -132,7 +129,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return3->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -166,7 +162,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return4->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -203,7 +198,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return5->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -246,7 +240,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return6->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -280,7 +273,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return7->returnReference' - locale: null workflowItem: currentStep: name: pending @@ -317,7 +309,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return8->returnReference' - locale: null workflowItem: currentStep: name: pending diff --git a/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/get_return_by_id.yml b/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/get_return_by_id.yml index eff1724a1..664f6cf63 100644 --- a/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/get_return_by_id.yml +++ b/src/Marello/Bundle/ReturnBundle/Tests/Functional/Api/responses/get_return_by_id.yml @@ -5,7 +5,6 @@ data: returnNumber: 'returnNumber)>' salesChannelName: channel1 returnReference: '@return1->returnReference' - locale: null workflowItem: currentStep: name: pending diff --git a/src/Marello/Bundle/ReturnBundle/Twig/ReturnExtension.php b/src/Marello/Bundle/ReturnBundle/Twig/ReturnExtension.php index b207f8218..e707d077b 100644 --- a/src/Marello/Bundle/ReturnBundle/Twig/ReturnExtension.php +++ b/src/Marello/Bundle/ReturnBundle/Twig/ReturnExtension.php @@ -6,8 +6,10 @@ use Marello\Bundle\ReturnBundle\Entity\ReturnEntity; use Marello\Bundle\ReturnBundle\Util\ReturnHelper; use Oro\Bundle\WorkflowBundle\Model\WorkflowManager; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class ReturnExtension extends \Twig_Extension +class ReturnExtension extends AbstractExtension { const NAME = 'marello_return'; @@ -47,11 +49,11 @@ public function getName() public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_return_get_order_item_returned_quantity', [$this, 'getOrderItemReturnedQuantity'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_return_is_on_hold', [$this, 'isOnHold'] ), diff --git a/src/Marello/Bundle/ReturnBundle/Workflow/InspectionAction.php b/src/Marello/Bundle/ReturnBundle/Workflow/InspectionAction.php index 8ebe8d027..6ccb9e1fd 100644 --- a/src/Marello/Bundle/ReturnBundle/Workflow/InspectionAction.php +++ b/src/Marello/Bundle/ReturnBundle/Workflow/InspectionAction.php @@ -2,19 +2,16 @@ namespace Marello\Bundle\ReturnBundle\Workflow; +use Marello\Bundle\InventoryBundle\Event\InventoryUpdateEvent; use Marello\Bundle\InventoryBundle\Model\InventoryUpdateContextFactory; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - +use Marello\Bundle\OrderBundle\Entity\OrderItem; +use Marello\Bundle\ReturnBundle\Entity\ReturnEntity; +use Marello\Bundle\ReturnBundle\Entity\ReturnItem; use Oro\Bundle\WorkflowBundle\Entity\WorkflowItem; -use Oro\Component\ConfigExpression\ContextAccessor; use Oro\Component\Action\Action\AbstractAction; use Oro\Component\Action\Action\ActionInterface; - -use Marello\Bundle\ReturnBundle\Entity\ReturnEntity; -use Marello\Bundle\ReturnBundle\Entity\ReturnItem; -use Marello\Bundle\OrderBundle\Entity\OrderItem; -use Marello\Bundle\InventoryBundle\Event\InventoryUpdateEvent; -use Marello\Bundle\InventoryBundle\Model\InventoryUpdateContext; +use Oro\Component\ConfigExpression\ContextAccessor; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; class InspectionAction extends AbstractAction { diff --git a/src/Marello/Bundle/RuleBundle/Datagrid/Extension/MassAction/StatusMassActionHandler.php b/src/Marello/Bundle/RuleBundle/Datagrid/Extension/MassAction/StatusMassActionHandler.php index f6bbe37c5..30534934d 100644 --- a/src/Marello/Bundle/RuleBundle/Datagrid/Extension/MassAction/StatusMassActionHandler.php +++ b/src/Marello/Bundle/RuleBundle/Datagrid/Extension/MassAction/StatusMassActionHandler.php @@ -8,7 +8,7 @@ use Oro\Bundle\DataGridBundle\Extension\MassAction\MassActionHandlerArgs; use Oro\Bundle\DataGridBundle\Extension\MassAction\MassActionHandlerInterface; use Oro\Bundle\DataGridBundle\Extension\MassAction\MassActionResponse; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class StatusMassActionHandler implements MassActionHandlerInterface { diff --git a/src/Marello/Bundle/RuleBundle/Entity/Rule.php b/src/Marello/Bundle/RuleBundle/Entity/Rule.php index 85389aa3f..2d01db61e 100644 --- a/src/Marello/Bundle/RuleBundle/Entity/Rule.php +++ b/src/Marello/Bundle/RuleBundle/Entity/Rule.php @@ -143,7 +143,7 @@ class Rule extends ExtendRule implements DatesAwareInterface, RuleInterface /** * @var bool * - * @ORM\Column(name="system", type="boolean", nullable=false, options={"default"=false}) + * @ORM\Column(name="is_system", type="boolean", nullable=false, options={"default"=false}) * @ConfigField( * defaultValues={ * "dataaudit"={ diff --git a/src/Marello/Bundle/RuleBundle/Migrations/Schema/MarelloRuleBundleInstaller.php b/src/Marello/Bundle/RuleBundle/Migrations/Schema/MarelloRuleBundleInstaller.php index 30b6abfd1..f00831fb0 100644 --- a/src/Marello/Bundle/RuleBundle/Migrations/Schema/MarelloRuleBundleInstaller.php +++ b/src/Marello/Bundle/RuleBundle/Migrations/Schema/MarelloRuleBundleInstaller.php @@ -13,7 +13,7 @@ class MarelloRuleBundleInstaller implements Installation */ public function getMigrationVersion() { - return 'v1_1'; + return 'v1_2'; } /** @@ -36,7 +36,7 @@ protected function createMarelloRuleTable(Schema $schema) $table->addColumn('sort_order', 'integer', []); $table->addColumn('stop_processing', 'boolean', ['default' => false]); $table->addColumn('expression', 'text', ['notnull' => false]); - $table->addColumn('system', 'boolean', ['default' => false]); + $table->addColumn('is_system', 'boolean', ['default' => false]); $table->addColumn('created_at', 'datetime', []); $table->addColumn('updated_at', 'datetime', []); $table->setPrimaryKey(['id']); diff --git a/src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleAddIsSystemColumn.php b/src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleAddIsSystemColumn.php new file mode 100644 index 000000000..5cd6834b4 --- /dev/null +++ b/src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleAddIsSystemColumn.php @@ -0,0 +1,44 @@ +updateMarelloRuleTable($schema, $queries); + } + + /** + * @param Schema $schema + * @param QueryBag $queries + */ + protected function updateMarelloRuleTable(Schema $schema, QueryBag $queries) + { + $table = $schema->getTable('marello_rule'); + + $table->addColumn('is_system', 'boolean', ['default' => false]); + $query = " + UPDATE marello_rule + SET + is_system = system"; + $queries->addQuery($query); + } +} diff --git a/src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleDropSystemColumn.php b/src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleDropSystemColumn.php new file mode 100644 index 000000000..0635d1fa3 --- /dev/null +++ b/src/Marello/Bundle/RuleBundle/Migrations/Schema/v1_2/MarelloRuleBundleDropSystemColumn.php @@ -0,0 +1,39 @@ +updateMarelloRuleTable($schema); + } + + /** + * @param Schema $schema + */ + protected function updateMarelloRuleTable(Schema $schema) + { + $table = $schema->getTable('marello_rule'); + + $table->dropColumn('system'); + + } +} diff --git a/src/Marello/Bundle/RuleBundle/Resources/config/services.yml b/src/Marello/Bundle/RuleBundle/Resources/config/services.yml index 0f5017c01..eb8f2dbbc 100644 --- a/src/Marello/Bundle/RuleBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/RuleBundle/Resources/config/services.yml @@ -1,6 +1,3 @@ -parameters: - marello_rule.entity.rule.class: 'Marello\Bundle\RuleBundle\Entity\Rule' - services: marello_rule.expression_language: class: 'Oro\Component\ExpressionLanguage\ExpressionLanguage' @@ -41,3 +38,15 @@ services: marello_rule.action.visibility_provider: class: 'Marello\Bundle\RuleBundle\Datagrid\RuleActionsVisibilityProvider' + + marello_rule.basic_expression_language_validator: + class: 'Oro\Component\ExpressionLanguage\BasicExpressionLanguageValidator' + arguments: + - '@marello_rule.expression_language' + + marello_rule.validator_constraints.expression_language_syntax_validator: + class: 'Marello\Bundle\RuleBundle\Validator\Constraints\ExpressionLanguageSyntaxValidator' + arguments: + - '@marello_rule.basic_expression_language_validator' + tags: + - { name: validator.constraint_validator, alias: oro_rule.validator_constraints.expression_language_syntax_validator } diff --git a/src/Marello/Bundle/RuleBundle/Validator/Constraints/ExpressionLanguageSyntax.php b/src/Marello/Bundle/RuleBundle/Validator/Constraints/ExpressionLanguageSyntax.php new file mode 100644 index 000000000..784b29ffb --- /dev/null +++ b/src/Marello/Bundle/RuleBundle/Validator/Constraints/ExpressionLanguageSyntax.php @@ -0,0 +1,16 @@ +basicExpressionLanguageValidator = $basicExpressionLanguageValidator; + } + + /** + * @param string $expression + * @param Constraint $constraint + */ + public function validate($expression, Constraint $constraint) + { + if ($expression) { + $validateResult = $this->basicExpressionLanguageValidator->validate($expression); + if ($validateResult) { + $this->context->addViolation($validateResult); + } + } + } +} diff --git a/src/Marello/Bundle/SalesBundle/Config/SalesChannelScopeManager.php b/src/Marello/Bundle/SalesBundle/Config/SalesChannelScopeManager.php new file mode 100644 index 000000000..0745354d9 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Config/SalesChannelScopeManager.php @@ -0,0 +1,40 @@ +dispatchScopeIdChangeEvent(); + + $this->scopeId = $scopeId; + } + + public function getScopeId() + { + return $this->scopeId; + } + + protected function isSupportedScopeEntity($entity) + { + return $entity instanceof SalesChannel; + } + + protected function getScopeEntityIdValue($entity) + { + return $entity->getId(); + } +} diff --git a/src/Marello/Bundle/SalesBundle/Controller/ConfigController.php b/src/Marello/Bundle/SalesBundle/Controller/ConfigController.php new file mode 100644 index 000000000..c036e3bd6 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Controller/ConfigController.php @@ -0,0 +1,84 @@ +get('marello_sales.config_form_provider.saleschannel'); + /** @var ConfigManager $manager */ + $manager = $this->get('oro_config.saleschannel'); + $prevScopeId = $manager->getScopeId(); + $manager->setScopeIdFromEntity($entity); + + list($activeGroup, $activeSubGroup) = $provider->chooseActiveGroups($activeGroup, $activeSubGroup); + + $jsTree = $provider->getJsTree(); + $form = false; + + if ($activeSubGroup !== null) { + $form = $provider->getForm($activeSubGroup); + + if ($this->get('oro_config.form.handler.config') + ->setConfigManager($manager) + ->process($form, $request) + ) { + $this->get('session')->getFlashBag()->add( + 'success', + $this->get('translator')->trans('oro.config.controller.config.saved.message') + ); + + // outdate content tags, it's only special case for generation that are not covered by NavigationBundle + $taggableData = ['name' => 'saleschannel_configuration', 'params' => [$activeGroup, $activeSubGroup]]; + $tagGenerator = $this->get('oro_sync.content.tag_generator'); + $dataUpdateTopicSender = $this->get('oro_sync.content.data_update_topic_sender'); + + $dataUpdateTopicSender->send($tagGenerator->generate($taggableData)); + + // recreate form to drop values for fields with use_parent_scope_value + $form = $provider->getForm($activeSubGroup); + $form->setData($manager->getSettingsByForm($form)); + } + } + $manager->setScopeId($prevScopeId); + + return [ + 'entity' => $entity, + 'data' => $jsTree, + 'form' => $form ? $form->createView() : null, + 'activeGroup' => $activeGroup, + 'activeSubGroup' => $activeSubGroup, + 'scopeEntity' => $entity, + 'scopeEntityClass' => SalesChannel::class, + 'scopeEntityId' => $entity->getId() + ]; + } +} diff --git a/src/Marello/Bundle/SalesBundle/Controller/SalesChannelController.php b/src/Marello/Bundle/SalesBundle/Controller/SalesChannelController.php index 74d4225c2..4d97d6d16 100644 --- a/src/Marello/Bundle/SalesBundle/Controller/SalesChannelController.php +++ b/src/Marello/Bundle/SalesBundle/Controller/SalesChannelController.php @@ -6,17 +6,21 @@ use Marello\Bundle\SalesBundle\Form\Type\SalesChannelType; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Oro\Bundle\SecurityBundle\Annotation\Acl; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Annotation\Route; -class SalesChannelController extends Controller +class SalesChannelController extends AbstractController { /** - * @Config\Route("/", name="marello_sales_saleschannel_index") - * @Config\Method("GET") - * @Config\Template + * @Route( + * path="/", + * methods={"GET"}, + * name="marello_sales_saleschannel_index" + * ) + * @Template * @AclAncestor("marello_sales_saleschannel_view") */ public function indexAction() @@ -27,9 +31,12 @@ public function indexAction() } /** - * @Config\Route("/create", name="marello_sales_saleschannel_create") - * @Config\Method({"GET", "POST"}) - * @Config\Template("MarelloSalesBundle:SalesChannel:update.html.twig") + * @Route( + * path="/create", + * methods={"GET", "POST"}, + * name="marello_sales_saleschannel_create" + * ) + * @Template("MarelloSalesBundle:SalesChannel:update.html.twig") * @AclAncestor("marello_saleschannel_create") * * @param Request $request @@ -41,8 +48,12 @@ public function createAction(Request $request) } /** - * @Config\Route("/view/{id}", name="marello_sales_saleschannel_view", requirements={"id"="\d+"}) - * @Config\Template + * @Route( + * path="/view/{id}", + * name="marello_sales_saleschannel_view", + * requirements={"id"="\d+"} + * ) + * @Template * @Acl( * id="marello_sales_saleschannel_view", * type="entity", @@ -61,9 +72,13 @@ public function viewAction(SalesChannel $salesChannel) } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_sales_saleschannel_update") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/update/{id}", + * methods={"GET", "POST"}, + * requirements={"id"="\d+"}, + * name="marello_sales_saleschannel_update" + * ) + * @Template * @AclAncestor("marello_saleschannel_update") * * @param Request $request diff --git a/src/Marello/Bundle/SalesBundle/Controller/SalesChannelGroupController.php b/src/Marello/Bundle/SalesBundle/Controller/SalesChannelGroupController.php index 749b7a44e..53887c889 100644 --- a/src/Marello/Bundle/SalesBundle/Controller/SalesChannelGroupController.php +++ b/src/Marello/Bundle/SalesBundle/Controller/SalesChannelGroupController.php @@ -6,16 +6,19 @@ use Marello\Bundle\SalesBundle\Form\Type\SalesChannelGroupType; use Oro\Bundle\SecurityBundle\Annotation\Acl; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; +use Symfony\Component\Routing\Annotation\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; -class SalesChannelGroupController extends Controller +class SalesChannelGroupController extends AbstractController { /** - * @Route("/", name="marello_sales_saleschannelgroup_index") + * @Route( + * path="/", + * name="marello_sales_saleschannelgroup_index" + * ) * @Template * @AclAncestor("marello_sales_saleschannelgroup_view") * @@ -29,7 +32,10 @@ public function indexAction() } /** - * @Route("/create", name="marello_sales_saleschannelgroup_create") + * @Route( + * path="/create", + * name="marello_sales_saleschannelgroup_create" + * ) * @Template("MarelloSalesBundle:SalesChannelGroup:update.html.twig") * @Acl( * id="marello_sales_saleschannelgroup_create", @@ -47,7 +53,11 @@ public function createAction(Request $request) } /** - * @Route("/view/{id}", name="marello_sales_saleschannelgroup_view", requirements={"id"="\d+"}) + * @Route( + * path="/view/{id}", + * name="marello_sales_saleschannelgroup_view", + * requirements={"id"="\d+"} + * ) * @Template * @Acl( * id="marello_sales_saleschannelgroup_view", @@ -68,7 +78,11 @@ public function viewAction(SalesChannelGroup $salesChannelGroup) } /** - * @Route("/update/{id}", name="marello_sales_saleschannelgroup_update", requirements={"id"="\d+"}) + * @Route( + * path="/update/{id}", + * name="marello_sales_saleschannelgroup_update", + * requirements={"id"="\d+"} + * ) * @Template * @Acl( * id="marello_sales_saleschannelgroup_update", diff --git a/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php b/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php index 35da69c5a..51294f5dc 100644 --- a/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php +++ b/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php @@ -4,7 +4,7 @@ use Doctrine\ORM\Mapping as ORM; use Marello\Bundle\CoreBundle\Model\EntityCreatedUpdatedAtTrait; -use Marello\Bundle\LocaleBundle\Model\LocaleAwareInterface; +use Marello\Bundle\LocaleBundle\Model\LocalizationAwareInterface; use Marello\Bundle\LocaleBundle\Model\LocalizationTrait; use Marello\Bundle\PricingBundle\Model\CurrencyAwareInterface; use Marello\Bundle\SalesBundle\Model\ExtendSalesChannel; @@ -48,7 +48,7 @@ */ class SalesChannel extends ExtendSalesChannel implements CurrencyAwareInterface, - LocaleAwareInterface + LocalizationAwareInterface { use EntityCreatedUpdatedAtTrait; use LocalizationTrait; diff --git a/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php b/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php index ed65066f7..6db0b29a8 100644 --- a/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php +++ b/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php @@ -9,8 +9,6 @@ use Marello\Bundle\SalesBundle\Model\ExtendSalesChannelGroup; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\Config; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\ConfigField; -use Oro\Bundle\OrganizationBundle\Entity\Organization; -use Oro\Bundle\OrganizationBundle\Entity\OrganizationInterface; use Oro\Bundle\OrganizationBundle\Entity\Ownership\AuditableOrganizationAwareTrait; /** @@ -88,7 +86,7 @@ class SalesChannelGroup extends ExtendSalesChannelGroup /** * @var bool * - * @ORM\Column(name="system", type="boolean", nullable=false, options={"default"=false}) + * @ORM\Column(name="is_system", type="boolean", nullable=false, options={"default"=false}) * @ConfigField( * defaultValues={ * "dataaudit"={ diff --git a/src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelGridListener.php b/src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelGridListener.php new file mode 100644 index 000000000..6f230b87d --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelGridListener.php @@ -0,0 +1,32 @@ +getConfig(); + $config->offsetSetByPath( + '[properties][config_link]', + [ + 'type' => 'url', + 'route' => 'marello_sales_config_saleschannel', + 'params' => ['id'], + ] + ); + $config->offsetSetByPath( + '[actions][config]', + [ + 'type' => 'navigate', + 'label' => 'marello.sales.sales_channel.grid.action.config.label', + 'link' => 'config_link', + 'icon' => 'cog', + 'acl_resource' => 'marello_sales_saleschannel_update', + 'order' => 999, + ] + ); + } +} diff --git a/src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelsDatagridListener.php b/src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelsDatagridListener.php new file mode 100644 index 000000000..1da1ce645 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/EventListener/Datagrid/SalesChannelsDatagridListener.php @@ -0,0 +1,162 @@ +relatedEntityClass = $relatedEntityClass; + $this->salesChannelsChoicesProvider = $salesChannelsChoicesProvider; + + $this->expressionBuilder = new Expr(); + } + + /** + * @param BuildBefore $event + */ + public function onBuildBefore(BuildBefore $event) + { + $config = $event->getConfig(); + + $this->addSelect($config); + $this->addJoin($config); + $this->addColumn($config); + $this->addSorter($config); + $this->addFilter($config); + } + + /** + * @param DatagridConfiguration $configuration + * @return string + * @throws \InvalidArgumentException when a root entity not found in the grid + */ + protected function getAlias(DatagridConfiguration $configuration) + { + $rootAlias = $configuration->getOrmQuery()->getRootAlias(); + if (!$rootAlias) { + throw new \InvalidArgumentException( + sprintf( + 'A root entity is missing for grid "%s"', + $configuration->getName() + ) + ); + } + + return $rootAlias; + } + + /** + * @return string + */ + protected function getColumnLabel() + { + return 'marello.product.channels.label'; + } + + /** + * @return string + */ + protected function getDataName() + { + return self::DATA_NAME; + } + + /** + * @return string + */ + protected function getJoinAlias() + { + return self::JOIN_ALIAS; + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSelect(DatagridConfiguration $config) + { + $config->getOrmQuery()->addSelect( + sprintf('count(%s.code) AS channelsCount', $this->getJoinAlias()) + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addJoin(DatagridConfiguration $config) + { + $config->getOrmQuery()->addLeftJoin( + $this->getAlias($config).'.channels', + $this->getJoinAlias() + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addColumn(DatagridConfiguration $config) + { + $config->offsetSetByPath(sprintf('[columns][%s]', $this->getDataName()), [ + 'label' => $this->getColumnLabel(), + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => 'MarelloSalesBundle:SalesChannel/Datagrid/Property:channels.html.twig', + 'renderable' => false + ]); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSorter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[sorters][columns][%s]', $this->getDataName()), + ['data_name' => 'channelsCount'] + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addFilter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', $this->getDataName()), + [ + 'type' => 'choice', + 'data_name' => $this->getJoinAlias() . '.code', + 'enabled' => false, + 'options' => [ + 'field_options' => [ + 'multiple' => true, + 'choices' => $this->salesChannelsChoicesProvider->getChannels() + ] + ] + ] + ); + } +} diff --git a/src/Marello/Bundle/SalesBundle/EventListener/Doctrine/SalesChannelGroupInventoryRebalanceListener.php b/src/Marello/Bundle/SalesBundle/EventListener/Doctrine/SalesChannelGroupInventoryRebalanceListener.php index 53578ca70..06bb5ae60 100644 --- a/src/Marello/Bundle/SalesBundle/EventListener/Doctrine/SalesChannelGroupInventoryRebalanceListener.php +++ b/src/Marello/Bundle/SalesBundle/EventListener/Doctrine/SalesChannelGroupInventoryRebalanceListener.php @@ -94,19 +94,6 @@ public function getIsEntityInstanceOf($entity) return ($entity instanceof SalesChannelGroup); } - /** - * @deprecated since 2.1.1 will be removed in 3.0 use - * rebalanceForSalesChannelGroup(SalesChannelGroup $entity) instead - * @throws \Oro\Component\MessageQueue\Transport\Exception\Exception - */ - protected function triggerRebalance() - { - $this->messageProducer->send( - Topics::RESOLVE_REBALANCE_ALL_INVENTORY, - Topics::ALL_INVENTORY - ); - } - /** * {@inheritdoc} * @param SalesChannelGroup $entity diff --git a/src/Marello/Bundle/SalesBundle/Form/EventListener/DefaultSalesChannelSubscriber.php b/src/Marello/Bundle/SalesBundle/Form/EventListener/DefaultSalesChannelSubscriber.php index c638cf3a9..cc2749149 100644 --- a/src/Marello/Bundle/SalesBundle/Form/EventListener/DefaultSalesChannelSubscriber.php +++ b/src/Marello/Bundle/SalesBundle/Form/EventListener/DefaultSalesChannelSubscriber.php @@ -3,7 +3,7 @@ namespace Marello\Bundle\SalesBundle\Form\EventListener; use Doctrine\ORM\EntityManager; -use Marello\Bundle\SalesBundle\Model\SalesChannelAwareInterface; +use Marello\Bundle\SalesBundle\Model\SalesChannelsAwareInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; @@ -40,7 +40,7 @@ public function preSetData(FormEvent $event) { $entity = $event->getData(); if (!$entity || null === $entity->getId()) { - if ($entity instanceof SalesChannelAwareInterface) { + if ($entity instanceof SalesChannelsAwareInterface) { $channels = $this->getDefaultChannels(); if (!is_null($channels) && count($channels) !== 0) { foreach ($channels as $channel) { diff --git a/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php b/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php index c7d77f544..d08833760 100644 --- a/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php +++ b/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php @@ -2,12 +2,10 @@ namespace Marello\Bundle\SalesBundle\Form\Type; -use Doctrine\ORM\EntityRepository; use Marello\Bundle\PricingBundle\Form\EventListener\CurrencySubscriber; use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Oro\Bundle\CurrencyBundle\Form\Type\CurrencyType; use Oro\Bundle\LocaleBundle\Form\Type\LocalizationSelectType; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\FormBuilderInterface; diff --git a/src/Marello/Bundle/SalesBundle/Migrations/Data/ORM/LoadSalesChannelData.php b/src/Marello/Bundle/SalesBundle/Migrations/Data/ORM/LoadSalesChannelData.php index 5f44084c0..b876019f2 100644 --- a/src/Marello/Bundle/SalesBundle/Migrations/Data/ORM/LoadSalesChannelData.php +++ b/src/Marello/Bundle/SalesBundle/Migrations/Data/ORM/LoadSalesChannelData.php @@ -43,7 +43,6 @@ protected function loadSalesChannels() $channel->setCurrency($values['currency']); $channel->setOwner($organization); $channel->setLocalization($localization); - $channel->setLocale('nl_NL'); $this->manager->persist($channel); $i++; diff --git a/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php b/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php index 0949dcf15..359036121 100644 --- a/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php +++ b/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php @@ -3,7 +3,6 @@ namespace Marello\Bundle\SalesBundle\Migrations\Schema; use Doctrine\DBAL\Schema\Schema; -use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope; use Oro\Bundle\MigrationBundle\Migration\Installation; use Oro\Bundle\MigrationBundle\Migration\QueryBag; @@ -18,7 +17,7 @@ class MarelloSalesBundleInstaller implements Installation */ public function getMigrationVersion() { - return 'v1_1'; + return 'v1_2'; } /** @@ -44,7 +43,7 @@ protected function createMarelloSalesChannelGroupTable(Schema $schema) $table->addColumn('id', 'integer', ['autoincrement' => true]); $table->addColumn('name', 'string', ['length' => 255]); $table->addColumn('description', 'text', ['notnull' => false]); - $table->addColumn('system', 'boolean', ['default' => false]); + $table->addColumn('is_system', 'boolean', ['default' => false]); $table->addColumn('organization_id', 'integer', ['notnull' => false]); $table->addColumn('created_at', 'datetime', []); $table->addColumn('updated_at', 'datetime', ['notnull' => false]); @@ -70,7 +69,6 @@ protected function createMarelloSalesSalesChannelTable(Schema $schema) $table->addColumn('code', 'string', ['length' => 255]); $table->addColumn('currency', 'string', ['length' => 5]); $table->addColumn('localization_id', 'integer', ['notnull' => false]); - $table->addColumn('locale', 'string', ['notnull' => false, 'length' => 5]); $table->addColumn('group_id', 'integer', ['notnull' => false]); $table->addColumn('integration_channel_id', 'integer', ['notnull' => false]); $table->setPrimaryKey(['id']); diff --git a/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundle.php b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundle.php new file mode 100644 index 000000000..0cc87fa43 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundle.php @@ -0,0 +1,35 @@ +getTable('marello_sales_sales_channel'); + $table->dropColumn('locale'); + + $dropLocaleInConfigSql = <<addSql( + $dropLocaleInConfigSql, + ['field_name' => 'locale', 'class_name' => SalesChannel::class], + ['field_name' => Type::STRING, 'class_name' => Type::STRING] + ); + $queries->addPostQuery($dropLocaleInConfigQuery); + } +} diff --git a/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleAddIsSystemColumn.php b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleAddIsSystemColumn.php new file mode 100644 index 000000000..8fa1ee079 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleAddIsSystemColumn.php @@ -0,0 +1,44 @@ +updateMarelloSalesChannelGroupTable($schema, $queries); + } + + /** + * @param Schema $schema + * @param QueryBag $queries + */ + protected function updateMarelloSalesChannelGroupTable(Schema $schema, QueryBag $queries) + { + $table = $schema->getTable('marello_sales_channel_group'); + + $table->addColumn('is_system', 'boolean', ['default' => false]); + $query = " + UPDATE marello_sales_channel_group + SET + is_system = system"; + $queries->addQuery($query); + } +} diff --git a/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleDropSystemColumn.php b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleDropSystemColumn.php new file mode 100644 index 000000000..084b0b9b5 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_2/MarelloSalesBundleDropSystemColumn.php @@ -0,0 +1,39 @@ +updateMarelloSalesChannelGroupTable($schema); + } + + /** + * @param Schema $schema + */ + protected function updateMarelloSalesChannelGroupTable(Schema $schema) + { + $table = $schema->getTable('marello_sales_channel_group'); + + $table->dropColumn('system'); + + } +} diff --git a/src/Marello/Bundle/SalesBundle/Model/ChannelAwareInterface.php b/src/Marello/Bundle/SalesBundle/Model/ChannelAwareInterface.php deleted file mode 100644 index 8ef61f2c3..000000000 --- a/src/Marello/Bundle/SalesBundle/Model/ChannelAwareInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -getTreeData(self::CONFIG_KEY, self::CORRECT_FIELDS_NESTING_LEVEL); + } + + public function getJsTree() + { + return $this->getJsTreeData(self::CONFIG_KEY, self::CORRECT_MENU_NESTING_LEVEL); + } + + public function setParentCheckboxLabel($label) + { + $this->parentCheckboxLabel = $label; + } + + protected function getParentCheckboxLabel() + { + return $this->parentCheckboxLabel; + } +} diff --git a/src/Marello/Bundle/SalesBundle/Provider/SalesChannelLocalizationProvider.php b/src/Marello/Bundle/SalesBundle/Provider/SalesChannelLocalizationProvider.php index 85e241f9a..fb470f8a0 100644 --- a/src/Marello/Bundle/SalesBundle/Provider/SalesChannelLocalizationProvider.php +++ b/src/Marello/Bundle/SalesBundle/Provider/SalesChannelLocalizationProvider.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\SalesBundle\Provider; -use Marello\Bundle\LocaleBundle\Model\LocaleAwareInterface; +use Marello\Bundle\LocaleBundle\Model\LocalizationAwareInterface; use Marello\Bundle\LocaleBundle\Provider\EntityLocalizationProviderInterface; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderAwareInterface; @@ -13,7 +13,7 @@ class SalesChannelLocalizationProvider implements EntityLocalizationProviderInte /** * @inheritDoc */ - public function getLocalization(LocaleAwareInterface $entity) + public function getLocalization(LocalizationAwareInterface $entity) { if ($entity instanceof SalesChannel) { return $entity->getLocalization(); @@ -35,7 +35,7 @@ public function getLocalization(LocaleAwareInterface $entity) /** * @inheritDoc */ - public function isApplicable(LocaleAwareInterface $entity) + public function isApplicable(LocalizationAwareInterface $entity) { if ($entity instanceof SalesChannel || $entity instanceof Order || $entity instanceof OrderAwareInterface) { return true; diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/form.yml b/src/Marello/Bundle/SalesBundle/Resources/config/form.yml index 3cb311222..b1d16637b 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/config/form.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/config/form.yml @@ -1,49 +1,38 @@ -parameters: - #form types - marello_sales.form_type.saleschannel.class: Marello\Bundle\SalesBundle\Form\Type\SalesChannelType - marello_sales.form_type.saleschannel.select.class: Marello\Bundle\SalesBundle\Form\Type\SalesChannelSelectType - marello_sales.form_type.saleschannel.select.api.class: Marello\Bundle\SalesBundle\Form\Type\SalesChannelSelectApiType - marello_sales.form_type.saleschannel.multiselect.class: Marello\Bundle\SalesBundle\Form\Type\SalesChannelMultiSelectType - marello_sales.form_type.system_group_saleschannel.multiselect.class: Marello\Bundle\SalesBundle\Form\Type\SystemGroupSalesChannelMultiselectType - marello_sales.form_type.saleschannelgroup.class: Marello\Bundle\SalesBundle\Form\Type\SalesChannelGroupType - #form handlers - marello_sales.form_handler.saleschannel.class: Marello\Bundle\SalesBundle\Form\Handler\SalesChannelHandler - marello_sales.form_handler.saleschannelgroup.class: Marello\Bundle\SalesBundle\Form\Handler\SalesChannelGroupHandler services: #form types marello_sales_form_type.saleschannel: - class: '%marello_sales.form_type.saleschannel.class%' + class: 'Marello\Bundle\SalesBundle\Form\Type\SalesChannelType' arguments: - '@marello_productprice.pricing.form.event_listener.currency_subscriber' tags: - { name: form.type } marello_sales_form_type.saleschannelgroup: - class: '%marello_sales.form_type.saleschannelgroup.class%' + class: 'Marello\Bundle\SalesBundle\Form\Type\SalesChannelGroupType' tags: - { name: form.type } marello_sales_form_type.saleschannel_multi_select: - class: '%marello_sales.form_type.saleschannel.multiselect.class%' + class: 'Marello\Bundle\SalesBundle\Form\Type\SalesChannelMultiSelectType' arguments: - '@marello_sales.form.data_transformer.sales_channel' tags: - { name: form.type } marello_sales_form_type.system_group_saleschannel_multi_select: - class: '%marello_sales.form_type.system_group_saleschannel.multiselect.class%' + class: 'Marello\Bundle\SalesBundle\Form\Type\SystemGroupSalesChannelMultiselectType' arguments: - '@marello_sales.form.data_transformer.sales_channel' tags: - { name: form.type } marello_sales_form_type.saleschannel_select: - class: '%marello_sales.form_type.saleschannel.select.class%' + class: 'Marello\Bundle\SalesBundle\Form\Type\SalesChannelSelectType' tags: - { name: form.type } marello_sales.form.type.sales_channel_select_api: - class: '%marello_sales.form_type.saleschannel.select.api.class%' + class: 'Marello\Bundle\SalesBundle\Form\Type\SalesChannelSelectApiType' arguments: - '@marello_sales.form.data_transformer.sales_channel_to_code' tags: @@ -51,14 +40,14 @@ services: #form handlers marello_sales.saleschannel_form.handler: - class: '%marello_sales.form_handler.saleschannel.class%' + class: 'Marello\Bundle\SalesBundle\Form\Handler\SalesChannelHandler' arguments: - '@doctrine.orm.entity_manager' tags: - { name: oro_form.form.handler, alias: marello_sales.saleschannel_form.handler } marello_sales.saleschannelgroup_form.handler: - class: '%marello_sales.form_handler.saleschannelgroup.class%' + class: 'Marello\Bundle\SalesBundle\Form\Handler\SalesChannelGroupHandler' arguments: - '@doctrine.orm.entity_manager' tags: diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/SalesBundle/Resources/config/jsmodules.yml new file mode 100755 index 000000000..70ae2a2be --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Resources/config/jsmodules.yml @@ -0,0 +1,8 @@ +aliases: + oro/select2-autocomplete-system-group-sales-channels-component$: marellosales/js/app/components/select2-autocomplete-system-group-sales-channels-component +dynamic-imports: + marellosales: + - marellosales/js/app/components/sales-channel-select-component + - marellosales/js/app/components/select2-autocomplete-system-group-sales-channels-component + commons: + - oro/select2-autocomplete-system-group-sales-channels-component \ No newline at end of file diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/SalesBundle/Resources/config/oro/datagrids.yml index 676149d0b..b6b3c7b9b 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/config/oro/datagrids.yml @@ -61,7 +61,7 @@ datagrids: default: data_name: sc.default default: - name: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + name: "ASC" properties: id: ~ view_link: @@ -88,9 +88,6 @@ datagrids: - scg from: - { table: MarelloSalesBundle:SalesChannelGroup, alias: scg } - join: - left: - - { join: scg.salesChannels, alias: sc } where: and: - scg.system != true @@ -110,13 +107,13 @@ datagrids: columns: name: type: string - data_name: sc.name + data_name: scg.name sorters: columns: name: data_name: scg.name description: - data_name: sc.description + data_name: scg.description properties: id: ~ view_link: @@ -166,7 +163,13 @@ datagrids: type: orm query: select: - - sc + - sc.id + - sc.name + - sc.code + - sc.channelType + - sc.currency + - sc.active + - sc.default - (CASE WHEN (sc.id IN (:data_in)) AND sc.id NOT IN (:data_not_in) THEN true ELSE false END) as hasChannel from: - { table: MarelloSalesBundle:SalesChannel, alias: sc } @@ -229,7 +232,7 @@ datagrids: default: data_name: sc.default default: - name: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + name: "ASC" properties: id: ~ actions: ~ diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/oro/navigation.yml b/src/Marello/Bundle/SalesBundle/Resources/config/oro/navigation.yml index 55c94ccb5..dcbde0663 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/config/oro/navigation.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/config/oro/navigation.yml @@ -63,3 +63,4 @@ navigation: marello_sales_saleschannelgroup_create: 'oro.ui.create_entity' marello_sales_saleschannelgroup_update: '%name% - oro.ui.edit' marello_sales_saleschannelgroup_view: '%name% - oro.ui.view' + marello_sales_config_config: '%name% - marello.sales.config.saleschannel.label' diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/oro/placeholders.yml b/src/Marello/Bundle/SalesBundle/Resources/config/oro/placeholders.yml new file mode 100644 index 000000000..cc9c5c916 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Resources/config/oro/placeholders.yml @@ -0,0 +1,10 @@ +placeholders: + placeholders: + view_navButtons_before: + items: + saleschannel_config_button: ~ + + items: + saleschannel_config_button: + template: MarelloSalesBundle:Config:button.html.twig + applicable: "@marello_sales.placeholder.filter->isSalesChannelPage($entity$)" diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/oro/routing.yml b/src/Marello/Bundle/SalesBundle/Resources/config/oro/routing.yml index 204692d54..5bb19e1fd 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/config/oro/routing.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/config/oro/routing.yml @@ -3,6 +3,11 @@ marello_sales_saleschannel: type: annotation prefix: /marello/saleschannel +marello_sales_config: + resource: "@MarelloSalesBundle/Controller/ConfigController.php" + type: annotation + prefix: /config + marello_sales_saleschannelgroup: resource: "@MarelloSalesBundle/Controller/SalesChannelGroupController.php" type: annotation diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/requirejs.yml b/src/Marello/Bundle/SalesBundle/Resources/config/requirejs.yml deleted file mode 100755 index 80363c20d..000000000 --- a/src/Marello/Bundle/SalesBundle/Resources/config/requirejs.yml +++ /dev/null @@ -1,4 +0,0 @@ -config: - paths: - 'marellosales/js/app/components/sales-channel-select-component': 'bundles/marellosales/js/app/components/sales-channel-select-component.js' - 'oro/select2-autocomplete-system-group-sales-channels-component': 'bundles/marellosales/js/app/components/select2-autocomplete-system-group-sales-channels-component.js' diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/services.yml b/src/Marello/Bundle/SalesBundle/Resources/config/services.yml index 421ba4ab3..0ab8431a1 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/config/services.yml @@ -1,8 +1,3 @@ -parameters: - marello_sales.saleschannel.entity.class: Marello\Bundle\SalesBundle\Entity\SalesChannel - marello_sales.saleschannelgroup.entity.class: Marello\Bundle\SalesBundle\Entity\SalesChannelGroup - marello_sales.saleschannel.model.channel_aware_interface.class: Marello\Bundle\SalesBundle\Model\SalesChannelAwareInterface - services: marello_sales.form.event_listener.default_sales_channel_subscriber: class: Marello\Bundle\SalesBundle\Form\EventListener\DefaultSalesChannelSubscriber @@ -21,6 +16,7 @@ services: marello_sales.provider.basic_sales_channels_choices: class: Marello\Bundle\SalesBundle\Provider\BasicSalesChannelsChoicesProvider + public: true arguments: - '@oro_entity.doctrine_helper' @@ -36,8 +32,8 @@ services: marello_sales.saleschannel.form.autocomplete.search_handler: parent: oro_form.autocomplete.search_handler arguments: - - "%marello_sales.saleschannel.entity.class%" - - ["name"] + - 'Marello\Bundle\SalesBundle\Entity\SalesChannel' + - ['name'] tags: - { name: oro_form.autocomplete.search_handler, alias: saleschannels, acl_resource: marello_product_view } @@ -45,8 +41,8 @@ services: class: 'Marello\Bundle\SalesBundle\Autocomplete\ActiveSalesChannelHandler' parent: oro_form.autocomplete.search_handler arguments: - - "%marello_sales.saleschannel.entity.class%" - - ["name", "code", "channelType"] + - 'Marello\Bundle\SalesBundle\Entity\SalesChannel' + - ['name', 'code', 'channelType'] tags: - { name: oro_form.autocomplete.search_handler, alias: active_saleschannels, acl_resource: marello_product_view } @@ -54,8 +50,8 @@ services: class: 'Marello\Bundle\SalesBundle\Autocomplete\SystemGroupSalesChannelHandler' parent: oro_form.autocomplete.search_handler arguments: - - "%marello_sales.saleschannel.entity.class%" - - ["name"] + - 'Marello\Bundle\SalesBundle\Entity\SalesChannel' + - ['name'] tags: - { name: oro_form.autocomplete.search_handler, alias: system_group_saleschannels, acl_resource: marello_product_view } @@ -111,8 +107,17 @@ services: tags: - { name: kernel.event_listener, event: oro_datagrid.orm_datasource.result.after.marello-sales-channel-groups, method: onResultAfter } + marello_sales.event_listener.datagrid.products_grid: + class: 'Marello\Bundle\SalesBundle\EventListener\Datagrid\SalesChannelsDatagridListener' + arguments: + - 'Marello\Bundle\ProductBundle\Entity\Product' + - '@marello_sales.provider.basic_sales_channels_choices' + tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-products-grid, method: onBuildBefore } + marello_sales.datagrid.saleschannelgroup.action_permission_provider: class: 'Marello\Bundle\SalesBundle\Datagrid\SalesChannelGroupActionPermissionProvider' + public: true marello_sales.condition.sales_channel_has_valid_integration: class: 'Marello\Bundle\SalesBundle\Condition\HasValidIntegration' @@ -125,4 +130,25 @@ services: marello_sales.entity_localization_provider.sales_channel: class: 'Marello\Bundle\SalesBundle\Provider\SalesChannelLocalizationProvider' tags: - - {name: marello_entity_localization_provider, priority: 10} \ No newline at end of file + - {name: marello_entity_localization_provider, priority: 10} + + marello_sales.scope.saleschannel: + class: 'Marello\Bundle\SalesBundle\Config\SalesChannelScopeManager' + parent: oro_config.scope_manager.abstract + tags: + - { name: oro_config.scope, scope: saleschannel, priority: 60 } + + marello_sales.config_form_provider.saleschannel: + public: true + class: 'Marello\Bundle\SalesBundle\Provider\SalesChannelConfigurationFormProvider' + parent: 'oro_config.provider.abstract_provider' + lazy: true + + marello_sales.event_listener.saleschannel_grid_listener: + class: 'Marello\Bundle\SalesBundle\EventListener\Datagrid\SalesChannelGridListener' + tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-sales-channel, method: onBuildBefore } + + marello_sales.placeholder.filter: + public: true + class: 'Marello\Bundle\SalesBundle\Placeholder\PlaceholderFilter' diff --git a/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/sales-channel-select-component.js b/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/sales-channel-select-component.js index 65e0c1ec2..525a6ef33 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/sales-channel-select-component.js +++ b/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/sales-channel-select-component.js @@ -3,13 +3,12 @@ define(function(require) { 'use strict'; - var SalesChannelSelectComponent; - var $ = require('jquery'); - var _ = require('underscore'); - var BaseComponent = require('oroui/js/app/components/base/component'); - var mediator = require('oroui/js/mediator'); + const $ = require('jquery'); + const _ = require('underscore'); + const BaseComponent = require('oroui/js/app/components/base/component'); + const mediator = require('oroui/js/mediator'); - SalesChannelSelectComponent = BaseComponent.extend({ + const SalesChannelSelectComponent = BaseComponent.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/select2-autocomplete-system-group-sales-channels-component.js b/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/select2-autocomplete-system-group-sales-channels-component.js index 01394017c..4b7110f42 100755 --- a/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/select2-autocomplete-system-group-sales-channels-component.js +++ b/src/Marello/Bundle/SalesBundle/Resources/public/js/app/components/select2-autocomplete-system-group-sales-channels-component.js @@ -1,8 +1,8 @@ define(function (require) { 'use strict'; - var Select2AutocompleteSystemChannelGroupAwareComponent, + const Select2AutocompleteComponent = require('oro/select2-autocomplete-component'); - Select2AutocompleteSystemChannelGroupAwareComponent = Select2AutocompleteComponent.extend({ + const Select2AutocompleteSystemChannelGroupAwareComponent = Select2AutocompleteComponent.extend({ /** * @property {Object} diff --git a/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml index adfd26101..3813c94e5 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml @@ -2,6 +2,9 @@ marello: sales: navigation: sales.label: Sales + config: + saleschannel.label: Sales Channel Configuration + use_default.label: Use system saleschannel: header.label: Manage Sales Channels entity_label: Sales Channel @@ -14,13 +17,11 @@ marello: code.label: Code channel_type.label: Type owner.label: Owner - locale.label: Locale localization.label: Localization controller.message.saved: Sales Channel saved form.select_saleschannels: Select Sales Channels form.select_saleschannel: Select Sales Channel selected_channels.label: Selected Sales Channels - localization.label: Localization group.label: Group create_own_group.label: Create Sales Channel Group integration_channel.label: Integration Channel @@ -33,6 +34,9 @@ marello: shortcut_new_saleschannel.description: Create new sales channel instance shortcut_list_saleschannel.label: Show sales channel list shortcut_list_saleschannel.description: Show list of sales channels + grid: + action: + config.label: Sales Channel Configuration datablock: general: General @@ -54,4 +58,4 @@ marello: success: saved: Sales Channel Group has been saved successfully warning: - is_system_update_attempt: System Sales Channel Group can't be updated \ No newline at end of file + is_system_update_attempt: System Sales Channel Group can't be updated diff --git a/src/Marello/Bundle/SalesBundle/Resources/views/Config/button.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/Config/button.html.twig new file mode 100644 index 000000000..cbd071d95 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Resources/views/Config/button.html.twig @@ -0,0 +1,9 @@ +{% if is_granted('EDIT', entity) %} + {% import 'OroUIBundle::macros.html.twig' as UI %} + {{ UI.button({ + 'path' : path('marello_sales_config_saleschannel', { 'id': entity.id }), + 'label': 'oro.config.menu.system_configuration.label'|trans, + 'title': 'marello.sales.config.saleschannel.label'|trans, + 'iCss': 'fa-cog', + }) }} +{% endif %} diff --git a/src/Marello/Bundle/SalesBundle/Resources/views/Config/salesChannel.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/Config/salesChannel.html.twig new file mode 100644 index 000000000..cf66edf03 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Resources/views/Config/salesChannel.html.twig @@ -0,0 +1,24 @@ +{% extends 'OroConfigBundle::configPage.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} + +{% oro_title_set({params : {"%name%": entity.name }}) %} + +{% set pageTitle = [ + UI.link({ + 'path': path('marello_sales_saleschannel_index'), + 'label': 'marello.sales.saleschannel.entity_plural_label'|trans + }), + UI.link({ + 'path': path('marello_sales_saleschannel_view', {id: entity.id}), + 'label': entity.name + }), + 'marello.sales.config.saleschannel.label'|trans +] +%} + +{% set formAction = path( + 'marello_sales_config_saleschannel', + {id:entity.id, activeGroup: activeGroup, activeSubGroup: activeSubGroup} +) %} +{% set routeName = 'marello_sales_config_saleschannel' %} +{% set routeParameters = {id: entity.id} %} diff --git a/src/Marello/Bundle/SalesBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/Form/fields.html.twig index f2fb1320b..deac063b5 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/views/Form/fields.html.twig +++ b/src/Marello/Bundle/SalesBundle/Resources/views/Form/fields.html.twig @@ -1,91 +1,3 @@ -{% block marello_product_channel_tax_relation_form_widget %} - -
- {{ form_widget(form.salesChannel) }} -
- {{ form_errors(form.salesChannel) }} - - -
- {{ form_widget(form.taxCode) }} -
- {{ form_errors(form.taxCode) }} - -{% endblock %} - -{% macro marello_product_channel_tax_relation_collection_form_item_prototype(widget) %} - {% if 'collection' in widget.vars.block_prefixes %} - {% set form = widget.vars.prototype %} - {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} - {% set disabled = widget.vars.disabled %} - {% set allow_delete = widget.vars.allow_delete %} - {% else %} - {% if widget.vars.disallow_delete is defined and widget.vars.disallow_delete %} - {% set allow_delete = false %} - {% else %} - {% set allow_delete = widget.parent.vars.allow_delete %} - {% endif %} - {% set form = widget %} - {% set name = widget.vars.full_name %} - {% set disabled = widget.parent.vars.disabled %} - {% endif %} - - {% set page_component_options = { 'disabled': not allow_delete } %} - - - {{ form_widget(form) }} - {% if allow_delete %} - - - - {% elseif widget.parent.vars.allow_delete %} - - {% endif %} - -{% endmacro %} - -{% block marello_product_channel_tax_relation_collection_form_widget %} - {% spaceless %} - {% if prototype is defined %} - {% set prototype_html = _self.marello_product_channel_tax_relation_collection_form_item_prototype(form) %} - {% endif %} - {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'marello-item-collection grid-container' }) %} - {% set id = id ~ '_collection' %} -
- {% set prototype_name = form.vars.prototype_name %} -
- - - - - - {% if form.vars.allow_delete %} - - {% endif %} - - - - {% if form.children|length %} - {% for child in form.children %} - {{ _self.marello_product_channel_tax_relation_collection_form_item_prototype(child) }} - {% endfor %} - {% elseif show_form_when_empty and prototype_html is defined %} - {{ prototype_html|replace({(prototype_name): '0'})|raw }} - {% endif %} - -
{{ 'marello.product.productchanneltaxrelation.sales_channel.label'|trans }}{{ 'marello.product.productchanneltaxrelation.tax_code.label'|trans }}
-
- - {% if allow_add %} - {{ form.vars.add_label|default('marello.product.productchanneltaxrelation.form.add_productchanneltaxrelation')|trans }} - {% endif %} -
- {% endspaceless %} -{% endblock %} - {% block marello_sales_saleschannel_select_widget %}
{{ form_widget(form) }} diff --git a/src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/channels.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannel/Datagrid/Property/channels.html.twig similarity index 100% rename from src/Marello/Bundle/ProductBundle/Resources/views/Product/Datagrid/Property/channels.html.twig rename to src/Marello/Bundle/SalesBundle/Resources/views/SalesChannel/Datagrid/Property/channels.html.twig diff --git a/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannel/update.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannel/update.html.twig index d9b9d6a24..9cb5fd641 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannel/update.html.twig +++ b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannel/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% oro_title_set({params : {'%name%': entity.name, '%entityName%': 'marello.sales.saleschannel.entity_label'|trans }}) %} @@ -13,9 +14,9 @@ {{ parent() }} {{ UI.cancelButton(path('marello_sales_saleschannel_index')) }} - {% if entity.id and resource_granted('marello_sales_saleschannel_update') or resource_granted('marello_sales_saleschannel_create') %} + {% if entity.id and is_granted('marello_sales_saleschannel_update') or is_granted('marello_sales_saleschannel_create') %} {% set html = '' %} - {% if resource_granted('marello_sales_saleschannel_view') %} + {% if is_granted('marello_sales_saleschannel_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_sales_saleschannel_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/index.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/index.html.twig index 162a10543..96dcbede2 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/index.html.twig +++ b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/index.html.twig @@ -4,7 +4,7 @@ {% set pageTitle = 'marello.sales.saleschannelgroup.entity_plural_label'|trans %} {% block navButtons %} - {% if resource_granted('marello_sales_saleschannelgroup_create') %} + {% if is_granted('marello_sales_saleschannelgroup_create') %}
{{ UI.addButton({ 'path': path('marello_sales_saleschannelgroup_create'), diff --git a/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/update.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/update.html.twig index a6becdd24..60a03f27d 100755 --- a/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/update.html.twig +++ b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/update.html.twig @@ -1,5 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} - +{% import 'OroUIBundle::macros.html.twig' as UI %} {% set formAction = entity.id ? path('marello_sales_saleschannelgroup_update', {id: entity.id}) : path('marello_sales_saleschannelgroup_create') %} {% oro_title_set({params : {'%name%': entity.name, '%entityName%': 'marello.sales.saleschannelgroup.entity_label'|trans} }) %} @@ -7,9 +7,9 @@ {% block navButtons %} {{ parent() }} {{ UI.cancelButton(path('marello_sales_saleschannelgroup_index')) }} - {% if entity.id and resource_granted('marello_sales_saleschannelgroup_update') or resource_granted('marello_sales_saleschannelgroup_create') %} + {% if entity.id and is_granted('marello_sales_saleschannelgroup_update') or is_granted('marello_sales_saleschannelgroup_create') %} {% set html = '' %} - {% if resource_granted('marello_sales_saleschannelgroup_view') %} + {% if is_granted('marello_sales_saleschannelgroup_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_sales_saleschannelgroup_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/SalesBundle/Tests/Unit/Entity/SalesChannelTest.php b/src/Marello/Bundle/SalesBundle/Tests/Unit/Entity/SalesChannelTest.php index 8ca995035..478b9783e 100644 --- a/src/Marello/Bundle/SalesBundle/Tests/Unit/Entity/SalesChannelTest.php +++ b/src/Marello/Bundle/SalesBundle/Tests/Unit/Entity/SalesChannelTest.php @@ -27,8 +27,7 @@ public function testAccessors() ['channelType', 'some string'], ['createdAt', new \DateTime()], ['updatedAt', new \DateTime()], - ['localization', new Localization()], - ['locale', 'some string'] + ['localization', new Localization()] ]); } } diff --git a/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelGroupListenerTest.php b/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelGroupListenerTest.php index 523734e80..b39289f4b 100644 --- a/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelGroupListenerTest.php +++ b/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelGroupListenerTest.php @@ -10,6 +10,7 @@ use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Marello\Bundle\SalesBundle\Entity\SalesChannelGroup; use Marello\Bundle\SalesBundle\EventListener\Doctrine\SalesChannelGroupListener; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Session\Session; @@ -38,14 +39,14 @@ protected function setUp() * @dataProvider preRemoveDataProvider * @param int $flushQty * @param int $persistQty - * @param \PHPUnit_Framework_MockObject_MockObject|null $systemSalesChannelGroup - * @param \PHPUnit_Framework_MockObject_MockObject|null $systemLink + * @param MockObject|null $systemSalesChannelGroup + * @param MockObject|null $systemLink */ public function testPreRemove( $flushQty, $persistQty, - \PHPUnit_Framework_MockObject_MockObject $systemSalesChannelGroup = null, - \PHPUnit_Framework_MockObject_MockObject $systemLink = null + MockObject $systemSalesChannelGroup = null, + MockObject $systemLink = null ) { /** @var SalesChannel|\PHPUnit_Framework_MockObject_MockObject $salesChannel **/ $salesChannel = $this->createMock(SalesChannel::class); @@ -143,11 +144,11 @@ public function preRemoveDataProvider() /** * @dataProvider postPersistDataProvider * @param int $qty - * @param \PHPUnit_Framework_MockObject_MockObject|null $defaultLink + * @param MockObject|null $defaultLink */ - public function testPostPersist($qty, \PHPUnit_Framework_MockObject_MockObject $defaultLink = null) + public function testPostPersist($qty, MockObject $defaultLink = null) { - /** @var SalesChannelGroup|\PHPUnit_Framework_MockObject_MockObject $salesChannelGroup **/ + /** @var SalesChannelGroup|MockObject $salesChannelGroup **/ $salesChannelGroup = $this->createMock(SalesChannelGroup::class); $repository = $this->createMock(WarehouseChannelGroupLinkRepository::class); diff --git a/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelListenerTest.php b/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelListenerTest.php index f7ca4a9a4..060287816 100644 --- a/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelListenerTest.php +++ b/src/Marello/Bundle/SalesBundle/Tests/Unit/EventListener/Doctrine/SalesChannelListenerTest.php @@ -5,6 +5,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\LifecycleEventArgs; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Marello\Bundle\SalesBundle\Entity\SalesChannel; @@ -29,9 +30,9 @@ protected function setUp() /** * @dataProvider prePersistDataProvider - * @param \PHPUnit_Framework_MockObject_MockObject|null $salesChannelGroup + * @param MockObject|null $salesChannelGroup */ - public function testPrePersist(\PHPUnit_Framework_MockObject_MockObject $salesChannelGroup = null) + public function testPrePersist(MockObject $salesChannelGroup = null) { $salesChannel = new SalesChannel(); @@ -48,7 +49,7 @@ public function testPrePersist(\PHPUnit_Framework_MockObject_MockObject $salesCh ->with(SalesChannelGroup::class) ->willReturn($repository); - /** @var LifecycleEventArgs|\PHPUnit_Framework_MockObject_MockObject $args **/ + /** @var LifecycleEventArgs|MockObject $args **/ $args = $this ->getMockBuilder(LifecycleEventArgs::class) ->disableOriginalConstructor() diff --git a/src/Marello/Bundle/SalesBundle/Tests/Unit/Form/Handler/SalesChannelGroupHandlerTest.php b/src/Marello/Bundle/SalesBundle/Tests/Unit/Form/Handler/SalesChannelGroupHandlerTest.php index 4fa485e36..2c652766c 100644 --- a/src/Marello/Bundle/SalesBundle/Tests/Unit/Form/Handler/SalesChannelGroupHandlerTest.php +++ b/src/Marello/Bundle/SalesBundle/Tests/Unit/Form/Handler/SalesChannelGroupHandlerTest.php @@ -5,6 +5,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Persistence\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\ParameterBag; @@ -122,10 +123,10 @@ public function testProcess() } /** - * @param \PHPUnit_Framework_MockObject_MockObject $group - * @return \PHPUnit_Framework_MockObject_MockObject + * @param MockObject $group + * @return MockObject */ - private function mockSalesChannel(\PHPUnit_Framework_MockObject_MockObject $group) + private function mockSalesChannel(MockObject $group) { $salesChannel = $this->createMock(SalesChannel::class); $salesChannel diff --git a/src/Marello/Bundle/SalesBundle/Tests/Unit/Provider/SalesChannelLocalizationProviderTest.php b/src/Marello/Bundle/SalesBundle/Tests/Unit/Provider/SalesChannelLocalizationProviderTest.php index 52be9e137..3ebdef4a5 100644 --- a/src/Marello/Bundle/SalesBundle/Tests/Unit/Provider/SalesChannelLocalizationProviderTest.php +++ b/src/Marello/Bundle/SalesBundle/Tests/Unit/Provider/SalesChannelLocalizationProviderTest.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\SalesBundle\Tests\Unit\Provider; -use Marello\Bundle\LocaleBundle\Model\LocaleAwareInterface; +use Marello\Bundle\LocaleBundle\Model\LocalizationAwareInterface; use Marello\Bundle\OrderBundle\Entity\Order; use Marello\Bundle\OrderBundle\Entity\OrderAwareInterface; use Marello\Bundle\SalesBundle\Entity\SalesChannel; @@ -60,7 +60,7 @@ public function testOrderAwareEntity() ->expects(static::once()) ->method('getSalesChannel') ->willReturn($salesChannel); - $entity = $this->createMock([OrderAwareInterface::class, LocaleAwareInterface::class]); + $entity = $this->createMock([OrderAwareInterface::class, LocalizationAwareInterface::class]); $entity ->expects(static::once()) ->method('getOrder') diff --git a/src/Marello/Bundle/SalesBundle/Tests/Unit/Twig/SalesExtensionTest.php b/src/Marello/Bundle/SalesBundle/Tests/Unit/Twig/SalesExtensionTest.php index b9821d346..88ce57f1f 100644 --- a/src/Marello/Bundle/SalesBundle/Tests/Unit/Twig/SalesExtensionTest.php +++ b/src/Marello/Bundle/SalesBundle/Tests/Unit/Twig/SalesExtensionTest.php @@ -7,6 +7,7 @@ use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Marello\Bundle\SalesBundle\Twig\SalesExtension; use Marello\Bundle\SalesBundle\Entity\Repository\SalesChannelRepository; +use Twig\TwigFunction; class SalesExtensionTest extends TestCase { @@ -31,7 +32,7 @@ public function testGetFunctions() { static::assertEquals( [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_sales_has_active_channels', [$this->extension, 'checkActiveChannels'] ) diff --git a/src/Marello/Bundle/SalesBundle/Twig/SalesExtension.php b/src/Marello/Bundle/SalesBundle/Twig/SalesExtension.php index 70ea3332f..dc1926c1b 100644 --- a/src/Marello/Bundle/SalesBundle/Twig/SalesExtension.php +++ b/src/Marello/Bundle/SalesBundle/Twig/SalesExtension.php @@ -3,8 +3,10 @@ namespace Marello\Bundle\SalesBundle\Twig; use Marello\Bundle\SalesBundle\Entity\Repository\SalesChannelRepository; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class SalesExtension extends \Twig_Extension +class SalesExtension extends AbstractExtension { const NAME = 'marello_sales'; @@ -27,7 +29,7 @@ public function __construct(SalesChannelRepository $salesChannelRepository) public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_sales_has_active_channels', [$this, 'checkActiveChannels'] ) diff --git a/src/Marello/Bundle/ShippingBundle/Context/Builder/Basic/BasicShippingContextBuilder.php b/src/Marello/Bundle/ShippingBundle/Context/Builder/Basic/BasicShippingContextBuilder.php index 7610a09ff..d70f317ad 100644 --- a/src/Marello/Bundle/ShippingBundle/Context/Builder/Basic/BasicShippingContextBuilder.php +++ b/src/Marello/Bundle/ShippingBundle/Context/Builder/Basic/BasicShippingContextBuilder.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\ShippingBundle\Context\Builder\Basic; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ShippingBundle\Context\Builder\ShippingContextBuilderInterface; use Marello\Bundle\ShippingBundle\Context\LineItem\Collection\ShippingLineItemCollectionInterface; use Marello\Bundle\ShippingBundle\Context\ShippingContext; diff --git a/src/Marello/Bundle/ShippingBundle/Context/Builder/ShippingContextBuilderInterface.php b/src/Marello/Bundle/ShippingBundle/Context/Builder/ShippingContextBuilderInterface.php index 4e69acf2b..2ad03755e 100644 --- a/src/Marello/Bundle/ShippingBundle/Context/Builder/ShippingContextBuilderInterface.php +++ b/src/Marello/Bundle/ShippingBundle/Context/Builder/ShippingContextBuilderInterface.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\ShippingBundle\Context\Builder; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ShippingBundle\Context\LineItem\Collection\ShippingLineItemCollectionInterface; use Marello\Bundle\ShippingBundle\Context\ShippingContextInterface; use Oro\Bundle\CurrencyBundle\Entity\Price; diff --git a/src/Marello/Bundle/ShippingBundle/Context/ShippingContext.php b/src/Marello/Bundle/ShippingBundle/Context/ShippingContext.php index 19511dc4f..3fccf058c 100644 --- a/src/Marello/Bundle/ShippingBundle/Context/ShippingContext.php +++ b/src/Marello/Bundle/ShippingBundle/Context/ShippingContext.php @@ -3,7 +3,7 @@ namespace Marello\Bundle\ShippingBundle\Context; use Marello\Bundle\AddressBundle\Entity\MarelloAddress; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ShippingBundle\Context\LineItem\Collection\ShippingLineItemCollectionInterface; use Oro\Bundle\CurrencyBundle\Entity\Price; use Symfony\Component\HttpFoundation\ParameterBag; diff --git a/src/Marello/Bundle/ShippingBundle/Context/ShippingContextInterface.php b/src/Marello/Bundle/ShippingBundle/Context/ShippingContextInterface.php index 8916d4575..2b6a25b64 100644 --- a/src/Marello/Bundle/ShippingBundle/Context/ShippingContextInterface.php +++ b/src/Marello/Bundle/ShippingBundle/Context/ShippingContextInterface.php @@ -3,7 +3,7 @@ namespace Marello\Bundle\ShippingBundle\Context; use Marello\Bundle\AddressBundle\Entity\MarelloAddress; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ShippingBundle\Context\LineItem\Collection\ShippingLineItemCollectionInterface; use Oro\Bundle\CurrencyBundle\Entity\Price; diff --git a/src/Marello/Bundle/ShippingBundle/Controller/ShippingMethodsConfigsRuleController.php b/src/Marello/Bundle/ShippingBundle/Controller/ShippingMethodsConfigsRuleController.php index 397bd4be8..df524e7f9 100755 --- a/src/Marello/Bundle/ShippingBundle/Controller/ShippingMethodsConfigsRuleController.php +++ b/src/Marello/Bundle/ShippingBundle/Controller/ShippingMethodsConfigsRuleController.php @@ -8,16 +8,19 @@ use Marello\Bundle\ShippingBundle\Entity\ShippingMethodsConfigsRule; use Marello\Bundle\ShippingBundle\Form\Handler\ShippingMethodsConfigsRuleHandler; use Marello\Bundle\ShippingBundle\Form\Type\ShippingMethodsConfigsRuleType; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; +use Symfony\Component\Routing\Annotation\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -class ShippingMethodsConfigsRuleController extends Controller +class ShippingMethodsConfigsRuleController extends AbstractController { /** - * @Route("/", name="marello_shipping_methods_configs_rule_index") + * @Route( + * path="/", + * name="marello_shipping_methods_configs_rule_index" + * ) * @Template * @AclAncestor("marello_shipping_methods_configs_rule_view") * @@ -31,7 +34,10 @@ public function indexAction() } /** - * @Route("/create", name="marello_shipping_methods_configs_rule_create") + * @Route( + * path="/create", + * name="marello_shipping_methods_configs_rule_create" + * ) * @Template("MarelloShippingBundle:ShippingMethodsConfigsRule:update.html.twig") * @Acl( * id="marello_shipping_methods_configs_rule_create", @@ -49,7 +55,11 @@ public function createAction(Request $request) } /** - * @Route("/view/{id}", name="marello_shipping_methods_configs_rule_view", requirements={"id"="\d+"}) + * @Route( + * path="/view/{id}", + * name="marello_shipping_methods_configs_rule_view", + * requirements={"id"="\d+"} + * ) * @Template * @Acl( * id="marello_shipping_methods_configs_rule_view", @@ -70,7 +80,11 @@ public function viewAction(ShippingMethodsConfigsRule $shippingRule) } /** - * @Route("/update/{id}", name="marello_shipping_methods_configs_rule_update", requirements={"id"="\d+"}) + * @Route( + * path="/update/{id}", + * name="marello_shipping_methods_configs_rule_update", + * requirements={"id"="\d+"} + * ) * @Template * @Acl( * id="marello_shipping_methods_configs_rule_update", @@ -118,7 +132,10 @@ protected function update(ShippingMethodsConfigsRule $entity, Request $request) } /** - * @Route("/{gridName}/massAction/{actionName}", name="marello_status_shipping_rule_massaction") + * @Route( + * path="/{gridName}/massAction/{actionName}", + * name="marello_status_shipping_rule_massaction" + * ) * @Acl( * id="marello_shipping_methods_configs_rule_update", * type="entity", diff --git a/src/Marello/Bundle/ShippingBundle/Entity/HasShipmentTrait.php b/src/Marello/Bundle/ShippingBundle/Entity/HasShipmentTrait.php index b6c67ea9f..3cbfe6288 100644 --- a/src/Marello/Bundle/ShippingBundle/Entity/HasShipmentTrait.php +++ b/src/Marello/Bundle/ShippingBundle/Entity/HasShipmentTrait.php @@ -12,7 +12,7 @@ trait HasShipmentTrait protected $shipment; /** - * @return Shipment + * @return Shipment|null */ public function getShipment() { @@ -24,7 +24,7 @@ public function getShipment() * * @return $this */ - public function setShipment(?Shipment $shipment = null) + public function setShipment(Shipment $shipment = null) { $this->shipment = $shipment; diff --git a/src/Marello/Bundle/ShippingBundle/Entity/Repository/ShippingMethodsConfigsRuleRepository.php b/src/Marello/Bundle/ShippingBundle/Entity/Repository/ShippingMethodsConfigsRuleRepository.php index 33fe3bda4..dfc09113a 100755 --- a/src/Marello/Bundle/ShippingBundle/Entity/Repository/ShippingMethodsConfigsRuleRepository.php +++ b/src/Marello/Bundle/ShippingBundle/Entity/Repository/ShippingMethodsConfigsRuleRepository.php @@ -116,7 +116,7 @@ public function disableRulesWithoutShippingMethods() if (0 < count($rules)) { $enabledRulesIds = array_column($rules, 'id'); $qb = $this->createQueryBuilder('methodsConfigsRule'); - $qb->update('OroRuleBundle:Rule', 'rule') + $qb->update('MarelloRuleBundle:Rule', 'rule') ->set('rule.enabled', ':newValue') ->setParameter('newValue', false) ->where($qb->expr()->in('rule.id', ':rules')) diff --git a/src/Marello/Bundle/ShippingBundle/Form/Type/ShippingMethodsConfigsRuleType.php b/src/Marello/Bundle/ShippingBundle/Form/Type/ShippingMethodsConfigsRuleType.php index 2174ed34f..f4ae56066 100755 --- a/src/Marello/Bundle/ShippingBundle/Form/Type/ShippingMethodsConfigsRuleType.php +++ b/src/Marello/Bundle/ShippingBundle/Form/Type/ShippingMethodsConfigsRuleType.php @@ -2,20 +2,18 @@ namespace Marello\Bundle\ShippingBundle\Form\Type; +use Marello\Bundle\RuleBundle\Form\Type\RuleType; +use Marello\Bundle\ShippingBundle\Entity\ShippingMethodsConfigsRule; +use Marello\Bundle\ShippingBundle\Provider\ShippingMethodChoicesProviderInterface; +use Oro\Bundle\CurrencyBundle\Form\Type\CurrencySelectionType; +use Oro\Bundle\FormBundle\Form\Type\CollectionType; +use Oro\Bundle\FormBundle\Form\Type\OroChoiceType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; - -use Oro\Bundle\FormBundle\Form\Type\OroChoiceType; -use Oro\Bundle\FormBundle\Form\Type\CollectionType; -use Oro\Bundle\CurrencyBundle\Form\Type\CurrencySelectionType; - -use Marello\Bundle\RuleBundle\Form\Type\RuleType; -use Marello\Bundle\ShippingBundle\Entity\ShippingMethodsConfigsRule; -use Marello\Bundle\ShippingBundle\Provider\ShippingMethodChoicesProviderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class ShippingMethodsConfigsRuleType extends AbstractType { diff --git a/src/Marello/Bundle/ShippingBundle/Integration/ShippingAwareInterface.php b/src/Marello/Bundle/ShippingBundle/Integration/ShippingAwareInterface.php index e339ee7ea..17ad1d940 100644 --- a/src/Marello/Bundle/ShippingBundle/Integration/ShippingAwareInterface.php +++ b/src/Marello/Bundle/ShippingBundle/Integration/ShippingAwareInterface.php @@ -6,7 +6,14 @@ interface ShippingAwareInterface { + /** + * @return Shipment|null + */ public function getShipment(); - public function setShipment(?Shipment $shipment); + /** + * @param Shipment|null $shipment + * @return $this + */ + public function setShipment(Shipment $shipment = null); } diff --git a/src/Marello/Bundle/ShippingBundle/Method/Configuration/AllowUnlistedShippingMethodConfigurationInterface.php b/src/Marello/Bundle/ShippingBundle/Method/Configuration/AllowUnlistedShippingMethodConfigurationInterface.php deleted file mode 100644 index 312035993..000000000 --- a/src/Marello/Bundle/ShippingBundle/Method/Configuration/AllowUnlistedShippingMethodConfigurationInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -get(self::FIELD_ALLOW_UNLISTED_SHIPPING_METHOD, false); - } - - /** - * {@inheritdoc} - */ - public function isShippingMethodLocked() - { - return $this->get(self::FIELD_IS_SHIPPING_METHOD_LOCKED, false); - } - - /** - * {@inheritdoc} - */ - public function isOverriddenShippingCost() - { - return $this->get(self::FIELD_IS_OVERRIDDEN_SHIPPING_COST, false); - } - - /** - * {@inheritdoc} - */ - public function getShippingMethod() - { - return $this->get(self::FIELD_SHIPPING_METHOD); - } - - /** - * {@inheritdoc} - */ - public function getShippingMethodType() - { - return $this->get(self::FIELD_SHIPPING_METHOD_TYPE); - } - - /** - * {@inheritdoc} - */ - public function getShippingCost() - { - return $this->get(self::FIELD_SHIPPING_COST); - } -} diff --git a/src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilder.php b/src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilder.php deleted file mode 100644 index 02d581e40..000000000 --- a/src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilder.php +++ /dev/null @@ -1,145 +0,0 @@ -shippingMethod) { - $params[ComposedShippingMethodConfiguration::FIELD_SHIPPING_METHOD] = $this->shippingMethod; - } - - if (null !== $this->shippingMethodType) { - $params[ComposedShippingMethodConfiguration::FIELD_SHIPPING_METHOD_TYPE] = $this->shippingMethodType; - } - - if (null !== $this->shippingCost) { - $params[ComposedShippingMethodConfiguration::FIELD_SHIPPING_COST] = $this->shippingCost; - } - - if (null !== $this->isOverriddenCost) { - $params[ComposedShippingMethodConfiguration::FIELD_IS_OVERRIDDEN_SHIPPING_COST] = $this->isOverriddenCost; - } - - if (null !== $this->isShippingMethodLocked) { - $params[ComposedShippingMethodConfiguration::FIELD_IS_SHIPPING_METHOD_LOCKED] = - $this->isShippingMethodLocked; - } - - if (null !== $this->isAllowUnlistedShippingMethod) { - $params[ComposedShippingMethodConfiguration::FIELD_ALLOW_UNLISTED_SHIPPING_METHOD] = - $this->isAllowUnlistedShippingMethod; - } - - return new ComposedShippingMethodConfiguration($params); - } - - /** - * {@inheritdoc} - */ - public function buildShippingMethod( - PreConfiguredShippingMethodConfigurationInterface $preConfiguredShippingMethodConfiguration - ) { - $this->shippingMethod = $preConfiguredShippingMethodConfiguration->getShippingMethod(); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function buildShippingMethodType( - PreConfiguredShippingMethodConfigurationInterface $preConfiguredShippingMethodConfiguration - ) { - $this->shippingMethodType = $preConfiguredShippingMethodConfiguration->getShippingMethodType(); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function buildShippingCost( - PreConfiguredShippingMethodConfigurationInterface $preConfiguredShippingMethodConfiguration - ) { - $this->shippingCost = $preConfiguredShippingMethodConfiguration->getShippingCost(); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function buildIsOverriddenCost( - OverriddenCostShippingMethodConfigurationInterface $overriddenCostShippingMethodConfiguration - ) { - $this->isOverriddenCost = $overriddenCostShippingMethodConfiguration->isOverriddenShippingCost(); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function buildIsShippingMethodLocked( - MethodLockedShippingMethodConfigurationInterface $methodLockedShippingMethodConfiguration - ) { - $this->isShippingMethodLocked = $methodLockedShippingMethodConfiguration->isShippingMethodLocked(); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function buildIsAllowUnlistedShippingMethod( - AllowUnlistedShippingMethodConfigurationInterface $allowUnlistedShippingMethodConfiguration - ) { - $this->isAllowUnlistedShippingMethod = $allowUnlistedShippingMethodConfiguration - ->isAllowUnlistedShippingMethod(); - - return $this; - } -} diff --git a/src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilderFactory.php b/src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilderFactory.php deleted file mode 100644 index b92157933..000000000 --- a/src/Marello/Bundle/ShippingBundle/Method/Configuration/Composed/ComposedShippingMethodConfigurationBuilderFactory.php +++ /dev/null @@ -1,15 +0,0 @@ - {{ UI.addButton({ 'path': path('marello_shipping_methods_configs_rule_create'), diff --git a/src/Marello/Bundle/ShippingBundle/Resources/views/ShippingMethodsConfigsRule/update.html.twig b/src/Marello/Bundle/ShippingBundle/Resources/views/ShippingMethodsConfigsRule/update.html.twig index 330dc9c24..e1289c9b4 100755 --- a/src/Marello/Bundle/ShippingBundle/Resources/views/ShippingMethodsConfigsRule/update.html.twig +++ b/src/Marello/Bundle/ShippingBundle/Resources/views/ShippingMethodsConfigsRule/update.html.twig @@ -1,5 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} - +{% import 'OroUIBundle::macros.html.twig' as UI %} {% oro_title_set({params : {"%id%": entity.id|default('N/A'|trans)} }) %} {% set formAction = entity.id ? path('marello_shipping_methods_configs_rule_update', { 'id': entity.id }) : path('marello_shipping_methods_configs_rule_create') %} @@ -15,9 +15,9 @@ {{ parent() }} {% else %} {% set breadcrumbs = { - 'indexLabel': 'Create', - 'entityTitle': 'Shipping Rule', - 'indexPath': path('marello_shipping_methods_configs_rule_create') + 'indexLabel': 'Create', + 'entityTitle': 'Shipping Rule', + 'indexPath': path('marello_shipping_methods_configs_rule_create') } %} {% set title = 'oro.ui.create_entity'|trans({'%entityName%': 'marello.shipping.shippingmethodsconfigsrule.entity_label'|trans}) %} {% include 'OroUIBundle::page_title_block.html.twig' with { title: title } %} @@ -32,13 +32,13 @@ {% set html = '' %} {% if is_granted('marello_shipping_methods_configs_rule_view') %} {% set html = UI.saveAndCloseButton({ - 'route': 'marello_shipping_methods_configs_rule_view', - 'params': {'id': '$id'} + 'route': 'marello_shipping_methods_configs_rule_view', + 'params': {'id': '$id'} }) %} {% endif %} {% set html = html ~ UI.saveAndStayButton({ - 'route': 'marello_shipping_methods_configs_rule_update', - 'params': {'id': '$id'} + 'route': 'marello_shipping_methods_configs_rule_update', + 'params': {'id': '$id'} }) %} {{ UI.dropdownSaveButton({'html': html}) }} @@ -106,12 +106,6 @@ ] }] }, - { - 'title': 'marello.shipping.sections.shippingrule_conditions'|trans, - 'subblocks': [{ - 'data': [] - }] - }, { 'title': 'marello.shipping.sections.shippingrule_configurations.label'|trans, 'content_attr': { diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/Controller/ShippingMethodsConfigsRuleControllerTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Controller/ShippingMethodsConfigsRuleControllerTest.php new file mode 100644 index 000000000..74b38b9eb --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Controller/ShippingMethodsConfigsRuleControllerTest.php @@ -0,0 +1,516 @@ +initClient(); + $this->client->useHashNavigation(true); + $this->loadFixtures( + [ + LoadShippingMethodsConfigsRulesWithConfigs::class, + LoadUserData::class + ] + ); + $this->shippingMethodProvider = static::getContainer()->get('marello_shipping.shipping_method_provider'); + $this->translator = static::getContainer()->get('translator'); + } + + public function testIndexWithoutCreate() + { + $this->initClient([], static::generateBasicAuthHeader(LoadUserData::USER_VIEWER, LoadUserData::USER_VIEWER)); + $crawler = $this->client->request('GET', $this->getUrl('marello_shipping_methods_configs_rule_index')); + $result = $this->client->getResponse(); + static::assertHtmlResponseStatusCodeEquals($result, 200); + static::assertEquals(0, $crawler->selectLink('Create Shipping Rule')->count()); + } + + /** + * @return string + */ + public function testCreate() + { + $this->initClient( + [], + static::generateBasicAuthHeader(LoadUserData::USER_VIEWER_CREATOR, LoadUserData::USER_VIEWER_CREATOR) + ); + $crawler = $this->client->request('GET', $this->getUrl('marello_shipping_methods_configs_rule_create')); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + /** @var Form $form */ + $form = $crawler + ->selectButton('Save and Close') + ->form(); + + $name = 'New Rule'; + + $formValues = $form->getPhpValues(); + $formValues['marello_shipping_methods_configs_rule']['rule']['name'] = $name; + $formValues['marello_shipping_methods_configs_rule']['rule']['enabled'] = false; + $formValues['marello_shipping_methods_configs_rule']['currency'] = 'USD'; + $formValues['marello_shipping_methods_configs_rule']['rule']['sortOrder'] = 1; + $formValues['marello_shipping_methods_configs_rule']['destinations'] = + [ + [ + 'postalCodes' => '54321', + 'country' => 'FR', + 'region' => 'FR-75' + ] + ]; + $formValues['marello_shipping_methods_configs_rule']['methodConfigs'] = + [ + [ + 'method' => $this->getManualShippingIdentifier(), + 'options' => '', + 'typeConfigs' => [ + [ + 'enabled' => '1', + 'type' => 'primary', + 'options' => [ + 'price' => 12, + 'handling_fee' => null, + 'type' => 'per_item', + ], + ] + ] + ] + ]; + + $this->client->followRedirects(true); + $crawler = $this->client->request($form->getMethod(), $form->getUri(), $formValues); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + + $html = $crawler->html(); + + $this->assertContains('Shipping rule has been saved', $html); + $this->assertContains('No', $html); + + return $name; + } + + /** + * @depends testCreate + * + * @param string $name + */ + public function testIndex($name) + { + $auth = static::generateBasicAuthHeader(LoadUserData::USER_VIEWER_CREATOR, LoadUserData::USER_VIEWER_CREATOR); + $this->initClient([], $auth); + $crawler = $this->client->request('GET', $this->getUrl('marello_shipping_methods_configs_rule_index')); + $result = $this->client->getResponse(); + static::assertHtmlResponseStatusCodeEquals($result, 200); + static::assertContains('marello-shipping-methods-configs-rule-grid', $crawler->html()); + $href = $crawler->selectLink('Create Shipping Rule')->attr('href'); + static::assertEquals($this->getUrl('marello_shipping_methods_configs_rule_create'), $href); + + $response = $this->client->requestGrid( + [ + 'gridName' => 'marello-shipping-methods-configs-rule-grid', + 'marello-shipping-methods-configs-rule-grid[_sort_by][id]' => 'ASC', + ] + ); + + static::getJsonResponseContent($response, 200); + } + + /** + * @depends testCreate + * + * @param string $name + */ + public function testView($name) + { + $this->initClient([], static::generateBasicAuthHeader()); + $shippingRule = $this->getShippingMethodsConfigsRuleByName($name); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_shipping_methods_configs_rule_view', ['id' => $shippingRule->getId()]) + ); + + $result = $this->client->getResponse(); + static::assertHtmlResponseStatusCodeEquals($result, 200); + + $html = $crawler->html(); + + $this->assertContains($shippingRule->getRule()->getName(), $html); + $destination = $shippingRule->getDestinations(); + $this->assertContains((string)$destination[0], $html); + $methodConfigs = $shippingRule->getMethodConfigs(); + $label = $this->shippingMethodProvider + ->getShippingMethod($methodConfigs[0]->getMethod()) + ->getLabel(); + $this->assertContains($this->translator->trans($label), $html); + } + + /** + * @depends testCreate + * + * @param string $name + * + * @return ShippingMethodsConfigsRule|object|null + */ + public function testUpdate($name) + { + $shippingRule = $this->getShippingMethodsConfigsRuleByName($name); + + $this->assertNotEmpty($shippingRule); + + $id = $shippingRule->getId(); + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_shipping_methods_configs_rule_update', ['id' => $id]) + ); + + /** @var Form $form */ + $form = $crawler->selectButton('Save and Close')->form(); + + $newName = 'New name for new rule'; + $formValues = $form->getPhpValues(); + $formValues['marello_shipping_methods_configs_rule']['rule']['name'] = $newName; + $formValues['marello_shipping_methods_configs_rule']['rule']['enabled'] = false; + $formValues['marello_shipping_methods_configs_rule']['currency'] = 'USD'; + $formValues['marello_shipping_methods_configs_rule']['rule']['sortOrder'] = 1; + $formValues['marello_shipping_methods_configs_rule']['destinations'] = + [ + [ + 'postalCodes' => '54321', + 'country' => 'TH', + 'region' => 'TH-83' + ] + ]; + $formValues['marello_shipping_methods_configs_rule']['methodConfigs'] = + [ + [ + 'method' => $this->getManualShippingIdentifier(), + 'options' => '', + 'typeConfigs' => [ + [ + 'enabled' => '1', + 'type' => 'primary', + 'options' => [ + 'price' => 24, + 'handling_fee' => null, + 'type' => 'per_order', + ], + ] + ] + ] + ]; + + $this->client->followRedirects(true); + $crawler = $this->client->request($form->getMethod(), $form->getUri(), $formValues); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + $html = $crawler->html(); + static::assertContains('Shipping rule has been saved', $html); + + $shippingRule = $this->getShippingMethodsConfigsRuleByName($newName); + static::assertEquals($id, $shippingRule->getId()); + + $destination = $shippingRule->getDestinations(); + static::assertEquals('TH', $destination[0]->getCountry()->getIso2Code()); + static::assertEquals('TH-83', $destination[0]->getRegion()->getCombinedCode()); + static::assertEquals('54321', $destination[0]->getPostalCodes()->current()->getName()); + $methodConfigs = $shippingRule->getMethodConfigs(); + static::assertEquals($this->getManualShippingIdentifier(), $methodConfigs[0]->getMethod()); + static::assertEquals( + 24, + $methodConfigs[0]->getTypeConfigs()[0]->getOptions()['price'] + ); + static::assertFalse($shippingRule->getRule()->isEnabled()); + + return $shippingRule; + } + + /** + * @depends testUpdate + * + * @param ShippingMethodsConfigsRule $shippingRule + */ + public function testCancel(ShippingMethodsConfigsRule $shippingRule) + { + $shippingRule = $this->getShippingMethodsConfigsRuleByName($shippingRule->getRule()->getName()); + + $this->assertNotEmpty($shippingRule); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_shipping_methods_configs_rule_update', ['id' => $shippingRule->getId()]) + ); + + $link = $crawler->selectLink('Cancel')->link(); + $this->client->click($link); + $response = $this->client->getResponse(); + + static::assertHtmlResponseStatusCodeEquals($response, 200); + + $html = $response->getContent(); + + static::assertContains($shippingRule->getRule()->getName(), $html); + $destination = $shippingRule->getDestinations(); + static::assertContains((string)$destination[0], $html); + $methodConfigs = $shippingRule->getMethodConfigs(); + $label = $this->shippingMethodProvider + ->getShippingMethod($methodConfigs[0]->getMethod()) + ->getLabel(); + static::assertContains($this->translator->trans($label), $html); + } + + /** + * @depends testUpdate + * + * @param ShippingMethodsConfigsRule $shippingRule + * + * @return ShippingMethodsConfigsRule + */ + public function testUpdateRemoveDestination(ShippingMethodsConfigsRule $shippingRule) + { + $this->assertNotEmpty($shippingRule); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_shipping_methods_configs_rule_update', ['id' => $shippingRule->getId()]) + ); + + /** @var Form $form */ + $form = $crawler->selectButton('Save and Close')->form(); + + $formValues = $form->getPhpValues(); + $formValues['marello_shipping_methods_configs_rule']['destinations'] = []; + + $this->client->followRedirects(true); + $this->client->request($form->getMethod(), $form->getUri(), $formValues); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + $shippingRule = $this->getEntityManager()->find( + 'MarelloShippingBundle:ShippingMethodsConfigsRule', + $shippingRule->getId() + ); + static::assertCount(0, $shippingRule->getDestinations()); + + return $shippingRule; + } + + public function testStatusDisableMass() + { + $this->initClient([], static::generateBasicAuthHeader()); + /** @var ShippingMethodsConfigsRule $shippingRule1 */ + $shippingRule1 = $this->getReference('shipping_rule.1'); + /** @var ShippingMethodsConfigsRule $shippingRule2 */ + $shippingRule2 = $this->getReference('shipping_rule.2'); + $url = $this->getUrl( + 'marello_status_shipping_rule_massaction', + [ + 'gridName' => 'marello-shipping-methods-configs-rule-grid', + 'actionName' => 'disable', + 'inset' => 1, + 'values' => sprintf( + '%s,%s', + $shippingRule1->getId(), + $shippingRule2->getId() + ) + ] + ); + $this->ajaxRequest('GET', $url); + $result = $this->client->getResponse(); + $data = json_decode($result->getContent(), true); + $this->assertTrue($data['successful']); + $this->assertSame(2, $data['count']); + $this->assertFalse( + $this + ->getShippingMethodsConfigsRuleById($shippingRule1->getId()) + ->getRule() + ->isEnabled() + ); + $this->assertFalse( + $this + ->getShippingMethodsConfigsRuleById($shippingRule2->getId()) + ->getRule() + ->isEnabled() + ); + } + + /** + * @depends testStatusDisableMass + */ + public function testStatusEnableMass() + { + $this->initClient([], static::generateBasicAuthHeader()); + /** @var ShippingMethodsConfigsRule $shippingRule1 */ + $shippingRule1 = $this->getReference('shipping_rule.1'); + /** @var ShippingMethodsConfigsRule $shippingRule2 */ + $shippingRule2 = $this->getReference('shipping_rule.2'); + $url = $this->getUrl( + 'marello_status_shipping_rule_massaction', + [ + 'gridName' => 'marello-shipping-methods-configs-rule-grid', + 'actionName' => 'enable', + 'inset' => 1, + 'values' => sprintf( + '%s,%s', + $shippingRule1->getId(), + $shippingRule2->getId() + ) + ] + ); + $this->ajaxRequest('GET', $url); + $result = $this->client->getResponse(); + $data = json_decode($result->getContent(), true); + $this->assertTrue($data['successful']); + $this->assertSame(2, $data['count']); + $this->assertTrue( + $this + ->getShippingMethodsConfigsRuleById($shippingRule1->getId()) + ->getRule() + ->isEnabled() + ); + $this->assertTrue( + $this + ->getShippingMethodsConfigsRuleById($shippingRule2->getId()) + ->getRule() + ->isEnabled() + ); + } + + public function testShippingMethodsConfigsRuleEditWOPermission() + { + $authParams = static::generateBasicAuthHeader(LoadUserData::USER_VIEWER, LoadUserData::USER_VIEWER); + $this->initClient([], $authParams); + + /** @var ShippingMethodsConfigsRule $shippingRule */ + $shippingRule = $this->getReference('shipping_rule.1'); + + $this->client->request( + 'GET', + $this->getUrl('marello_shipping_methods_configs_rule_update', ['id' => $shippingRule->getId()]) + ); + + static::assertJsonResponseStatusCodeEquals($this->client->getResponse(), 403); + } + + public function testShippingMethodsConfigsRuleEdit() + { + $authParams = static::generateBasicAuthHeader(LoadUserData::USER_EDITOR, LoadUserData::USER_EDITOR); + $this->initClient([], $authParams); + + /** @var ShippingMethodsConfigsRule $shippingRule */ + $shippingRule = $this->getReference('shipping_rule.1'); + + $crawler = $this->client->request( + 'GET', + $this->getUrl('marello_shipping_methods_configs_rule_update', ['id' => $shippingRule->getId()]) + ); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + + /** @var Form $form */ + $form = $crawler->selectButton('Save')->form(); + + $rule = $shippingRule->getRule(); + $form['marello_shipping_methods_configs_rule[rule][enabled]'] = !$rule->isEnabled(); + $form['marello_shipping_methods_configs_rule[rule][name]'] = $rule->getName() . ' new name'; + $form['marello_shipping_methods_configs_rule[rule][sortOrder]'] = $rule->getSortOrder() + 1; + $form['marello_shipping_methods_configs_rule[currency]'] = $shippingRule->getCurrency() === 'USD' ? 'EUR' : 'USD'; + $form['marello_shipping_methods_configs_rule[rule][stopProcessing]'] = !$rule->isStopProcessing(); + $form['marello_shipping_methods_configs_rule[destinations][0][postalCodes]'] = '11111'; + $form['marello_shipping_methods_configs_rule[methodConfigs][0][typeConfigs][0][options][price]'] = 12; + $form['marello_shipping_methods_configs_rule[methodConfigs][0][typeConfigs][0][enabled]'] = true; + + $this->client->followRedirects(true); + $crawler = $this->client->submit($form); + + static::assertHtmlResponseStatusCodeEquals($this->client->getResponse(), 200); + static::assertContains('Shipping rule has been saved', $crawler->html()); + } + + public function testDeleteButtonNotVisible() + { + $authParams = static::generateBasicAuthHeader(LoadUserData::USER_VIEWER, LoadUserData::USER_VIEWER); + $this->initClient([], $authParams); + + $response = $this->client->requestGrid( + ['gridName' => 'marello-shipping-methods-configs-rule-grid'], + [], + true + ); + + $result = static::getJsonResponseContent($response, 200); + + $this->assertEquals(false, isset($result['metadata']['massActions']['delete'])); + } + + /** + * @return ObjectManager|null + */ + protected function getEntityManager() + { + return static::getContainer() + ->get('doctrine') + ->getManagerForClass('MarelloShippingBundle:ShippingMethodsConfigsRule'); + } + + /** + * @param string $name + * + * @return ShippingMethodsConfigsRule|null + */ + protected function getShippingMethodsConfigsRuleByName($name) + { + /** @var RuleInterface $rule */ + $rule = $this + ->getEntityManager() + ->getRepository('MarelloRuleBundle:Rule') + ->findOneBy(['name' => $name]); + + return $this + ->getEntityManager() + ->getRepository('MarelloShippingBundle:ShippingMethodsConfigsRule') + ->findOneBy(['rule' => $rule]); + } + + /** + * @param int $id + * + * @return ShippingMethodsConfigsRule|null + */ + protected function getShippingMethodsConfigsRuleById($id) + { + return $this->getEntityManager() + ->getRepository('MarelloShippingBundle:ShippingMethodsConfigsRule') + ->find($id); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodConfigsWithFakeMethods.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodConfigsWithFakeMethods.php new file mode 100644 index 000000000..04a52eba8 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodConfigsWithFakeMethods.php @@ -0,0 +1,83 @@ +getShippingMethodConfigsData() as $reference => $data) { + $this->loadShippingMethodConfig($reference, $data, $manager); + } + + $manager->flush(); + } + + /** + * @return array + */ + protected function getShippingMethodConfigsData() + { + return Yaml::parse(file_get_contents(__DIR__.'/data/shipping_method_configs_with_fake_methods.yml')); + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + */ + private function loadShippingMethodConfig($reference, $data, ObjectManager $manager) + { + $methodsConfigsRule = $this->getShippingMethodsConfigsRule($data['methods_configs_rule']); + + $methodConfig = $this->createMethodConfig($methodsConfigsRule, $data['method']); + + $manager->persist($methodConfig); + + $this->setReference($reference, $methodConfig); + } + + /** + * @param ShippingMethodsConfigsRule $configsRule + * @param string $method + * + * @return ShippingMethodConfig + */ + private function createMethodConfig(ShippingMethodsConfigsRule $configsRule, $method) + { + $methodConfig = new ShippingMethodConfig(); + + return $methodConfig->setMethodConfigsRule($configsRule) + ->setMethod($method); + } + + /** + * @param string $reference + * + * @return ShippingMethodsConfigsRule|object + */ + private function getShippingMethodsConfigsRule($reference) + { + return $this->getReference($reference); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodTypeConfigsWithFakeTypes.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodTypeConfigsWithFakeTypes.php new file mode 100644 index 000000000..32758593a --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodTypeConfigsWithFakeTypes.php @@ -0,0 +1,83 @@ +getShippingMethodTypeConfigsData() as $reference => $data) { + $this->loadShippingMethodTypeConfig($reference, $data, $manager); + } + + $manager->flush(); + } + + /** + * @return array + */ + protected function getShippingMethodTypeConfigsData() + { + return Yaml::parse(file_get_contents(__DIR__.'/data/shipping_method_type_configs_with_fake_types.yml')); + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + */ + private function loadShippingMethodTypeConfig($reference, $data, ObjectManager $manager) + { + $methodConfig = $this->getShippingMethodConfig($data['method_config']); + + $typeConfig = $this->createMethodTypeConfig($methodConfig, $data['type']); + + $manager->persist($typeConfig); + + $this->setReference($reference, $typeConfig); + } + + /** + * @param ShippingMethodConfig $methodConfig + * @param string $type + * + * @return ShippingMethodTypeConfig + */ + private function createMethodTypeConfig(ShippingMethodConfig $methodConfig, $type) + { + $configRule = new ShippingMethodTypeConfig(); + + return $configRule->setMethodConfig($methodConfig) + ->setType($type); + } + + /** + * @param string $reference + * + * @return ShippingMethodConfig|object + */ + private function getShippingMethodConfig($reference) + { + return $this->getReference($reference); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRules.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRules.php new file mode 100644 index 000000000..4d1b12eb2 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRules.php @@ -0,0 +1,79 @@ +getShippingMethodsConfigsRulesData() as $reference => $data) { + $this->loadShippingMethodsConfigsRule($reference, $data, $manager); + } + + $manager->flush(); + } + + /** + * @return array + */ + protected function getShippingMethodsConfigsRulesData() + { + return Yaml::parse(file_get_contents(__DIR__.'/data/shipping_methods_configs_rules.yml')); + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + */ + private function loadShippingMethodsConfigsRule($reference, $data, ObjectManager $manager) + { + $rule = $this->buildRule($reference, $data['rule']); + + $configRule = $this->createMethodsConfigsRule($rule, $data['currency']); + + $manager->persist($configRule); + + $this->setReference($reference, $configRule); + } + + /** + * @param RuleInterface $rule + * @param $currency + * + * @return ShippingMethodsConfigsRule + */ + private function createMethodsConfigsRule(RuleInterface $rule, $currency) + { + $configRule = new ShippingMethodsConfigsRule(); + + return $configRule->setRule($rule) + ->setCurrency($currency); + } + + /** + * @param string $reference + * @param array $ruleData + * + * @return RuleInterface + */ + private function buildRule($reference, $ruleData) + { + $rule = new Rule(); + + return $rule->setName($reference) + ->setEnabled($ruleData['enabled']) + ->setSortOrder($ruleData['sortOrder']) + ->setExpression($ruleData['expression']); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRulesWithConfigs.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRulesWithConfigs.php new file mode 100644 index 000000000..deda75a6e --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadShippingMethodsConfigsRulesWithConfigs.php @@ -0,0 +1,223 @@ +getShippingRuleData() as $reference => $data) { + $this->loadShippingRule($reference, $data, $manager); + } + + $manager->flush(); + } + + /** + * @return array + */ + protected function getShippingRuleData(): array + { + return Yaml::parse(file_get_contents(__DIR__.'/data/shipping_methods_configs_rules_with_configs.yml')); + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + */ + private function loadShippingRule($reference, $data, ObjectManager $manager) + { + $rule = $this->buildRule($reference, $data, $manager); + $configRule = $this->buildMethodsConfigsRule($reference, $data, $rule, $manager); + + $this->setReference($reference, $configRule); + + $manager->persist($configRule); + } + + /** + * @param string $reference + * @param array $data + * @param RuleInterface $rule + * @param ObjectManager $manager + * + * @return ShippingMethodsConfigsRule + */ + protected function buildMethodsConfigsRule( + string $reference, + array $data, + RuleInterface $rule, + ObjectManager $manager + ) { + $configRule = new ShippingMethodsConfigsRule(); + + $configRule + ->setRule($rule) + ->setCurrency($data['currency']) + ->setOrganization($this->getOrganization()); + + $this->setDestinations($configRule, $manager, $data); + $this->setMethodConfigs($configRule, $manager, $data); + + return $configRule; + } + + /** + * @param string $reference + * @param array $data + * @param ObjectManager $manager + * + * @return Rule + */ + protected function buildRule(string $reference, array $data, ObjectManager $manager) + { + $rule = new Rule(); + + $rule->setName($reference) + ->setEnabled($data['rule']['enabled']) + ->setSortOrder($data['rule']['sortOrder']) + ->setExpression($data['rule']['expression']); + + return $rule; + } + + /** + * @param ShippingMethodsConfigsRule $configRule + * @param ObjectManager $manager + * @param array $data + */ + private function setDestinations(ShippingMethodsConfigsRule $configRule, ObjectManager $manager, $data) + { + if (!array_key_exists('destinations', $data)) { + return; + } + + foreach ($data['destinations'] as $destination) { + /** @var Country $country */ + $country = $manager + ->getRepository('OroAddressBundle:Country') + ->findOneBy(['iso2Code' => $destination['country']]); + + $shippingRuleDestination = new ShippingMethodsConfigsRuleDestination(); + $shippingRuleDestination + ->setMethodConfigsRule($configRule) + ->setCountry($country); + + if (array_key_exists('region', $destination)) { + /** @var Region $region */ + $region = $manager + ->getRepository('OroAddressBundle:Region') + ->findOneBy(['combinedCode' => $destination['country'].'-'.$destination['region']]); + $shippingRuleDestination->setRegion($region); + } + + if (array_key_exists('postalCodes', $destination)) { + foreach ($destination['postalCodes'] as $postalCode) { + $destinationPostalCode = new ShippingMethodsConfigsRuleDestinationPostalCode(); + $destinationPostalCode->setName($postalCode['name']) + ->setDestination($shippingRuleDestination); + + $shippingRuleDestination->addPostalCode($destinationPostalCode); + } + } + + $manager->persist($shippingRuleDestination); + $configRule->addDestination($shippingRuleDestination); + } + } + + /** + * @param ShippingMethodsConfigsRule $configRule + * @param ObjectManager $manager + * @param array $data + */ + private function setMethodConfigs(ShippingMethodsConfigsRule $configRule, ObjectManager $manager, $data) + { + if (!array_key_exists('methodConfigs', $data)) { + return; + } + + foreach ($data['methodConfigs'] as $methodConfigData) { + $methodConfig = $this->buildMethodConfig($configRule); + + foreach ($methodConfigData['typeConfigs'] as $typeConfigData) { + $typeConfig = new ShippingMethodTypeConfig(); + $typeConfig->setType('primary') + ->setOptions([ + 'price' => $typeConfigData['options']['price'], + 'handling_fee' => null, + 'type' => $typeConfigData['options']['type'], + ]); + $typeConfig->setEnabled($typeConfigData['enabled']); + $methodConfig->addTypeConfig($typeConfig); + } + + $configRule->addMethodConfig($methodConfig); + + $manager->persist($methodConfig); + } + } + + /** + * @param ShippingMethodsConfigsRule $configRule + * + * @return ShippingMethodConfig + */ + private function buildMethodConfig(ShippingMethodsConfigsRule $configRule) + { + $methodConfig = new ShippingMethodConfig(); + + $methodConfig + ->setMethodConfigsRule($configRule) + ->setMethod($this->getManualShippingIdentifier()); + + return $methodConfig; + } + + /** + * @return Organization + */ + private function getOrganization() + { + return $this->container->get('doctrine') + ->getRepository('OroOrganizationBundle:Organization') + ->getFirst(); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadUserData.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadUserData.php new file mode 100644 index 000000000..662127d6b --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/LoadUserData.php @@ -0,0 +1,113 @@ + [ + [ + 'class' => ShippingMethodsConfigsRule::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'VIEW', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + [ + 'class' => Channel::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'VIEW', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + ], + self::ROLE_EDIT => [ + [ + 'class' => ShippingMethodsConfigsRule::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'EDIT', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + ], + self::ROLE_CREATE => [ + [ + 'class' => ShippingMethodsConfigsRule::class, + 'acls' => [ + [ + AbstractLoadMultipleUserData::ACL_PERMISSION => 'CREATE', + AbstractLoadMultipleUserData::ACL_LEVEL => 'SYSTEM', + ], + ], + ], + ], + ]; + + /** + * @var array + */ + protected $users = [ + [ + 'email' => 'shipping-user-viewer@example.com', + 'username' => self::USER_VIEWER, + 'password' => self::USER_VIEWER, + 'firstname' => 'ShippingUser1FN', + 'lastname' => 'ShippingUser1LN', + 'roles' => [self::ROLE_VIEW], + ], + [ + 'email' => 'shipping-user-editor@example.com', + 'username' => self::USER_EDITOR, + 'password' => self::USER_EDITOR, + 'firstname' => 'ShippingUser2FN', + 'lastname' => 'ShippingUser2LN', + 'roles' => [self::ROLE_VIEW, self::ROLE_EDIT], + ], + [ + 'email' => 'shipping-user-viewer-creator@example.com', + 'username' => self::USER_VIEWER_CREATOR, + 'password' => self::USER_VIEWER_CREATOR, + 'firstname' => 'ShippingUser2FN', + 'lastname' => 'ShippingUser2LN', + 'roles' => [self::ROLE_VIEW, self::ROLE_CREATE], + ] + ]; + + /** + * {@inheritdoc} + */ + protected function getRolesData() + { + return $this->roles; + } + + /** + * {@inheritdoc} + */ + protected function getUsersData() + { + return $this->users; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_configs_with_fake_methods.yml b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_configs_with_fake_methods.yml new file mode 100644 index 000000000..92cc165ec --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_configs_with_fake_methods.yml @@ -0,0 +1,15 @@ +shipping_rule.1.method_config.1: + methods_configs_rule: shipping_rule.1 + method: ups + +shipping_rule.2.method_config.1: + methods_configs_rule: shipping_rule.2 + method: flat_rate + +shipping_rule.3.method_config.1: + methods_configs_rule: shipping_rule.3 + method: dpd + +shipping_rule.3.method_config_without_type_configs: + methods_configs_rule: shipping_rule.3 + method: ups diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_type_configs_with_fake_types.yml b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_type_configs_with_fake_types.yml new file mode 100644 index 000000000..838270dee --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_method_type_configs_with_fake_types.yml @@ -0,0 +1,11 @@ +shipping_rule.1.method_config.1.type_config_1: + method_config: shipping_rule.1.method_config.1 + type: 3day + +shipping_rule.2.method_config.1.type_config_1: + method_config: shipping_rule.2.method_config.1 + type: primary + +shipping_rule.3.method_config.1.type_config_1: + method_config: shipping_rule.3.method_config.1 + type: classic diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules.yml b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules.yml new file mode 100644 index 000000000..15dd70cb3 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules.yml @@ -0,0 +1,27 @@ +shipping_rule.1: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'EUR' + +shipping_rule.2: + rule: + enabled: true + sortOrder: 1 + expression: 'true' + currency: 'EUR' + +shipping_rule.3: + rule: + enabled: false + sortOrder: 2 + expression: 'true' + currency: 'EUR' + +shipping_rule.4: + rule: + enabled: true + sortOrder: 3 + expression: 'true' + currency: 'EUR' diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules_with_configs.yml b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules_with_configs.yml new file mode 100644 index 000000000..76fc7a445 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/DataFixtures/data/shipping_methods_configs_rules_with_configs.yml @@ -0,0 +1,205 @@ +shipping_rule.1: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_order' + price: 10 + destinations: + - + postalCodes: + - + name: '12345' + country: 'US' + region: 'NY' +shipping_rule.2: + rule: + enabled: true + sortOrder: 1 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + country: 'US' + - + country: 'FR' + region: 'FR-75' +shipping_rule.3: + rule: + enabled: false + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + country: 'US' + region: 'NY' + - + country: 'FR' +shipping_rule.4: + rule: + enabled: true + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + postalCodes: + - + name: '12345' + - + name: '12346' + country: 'US' + region: 'NY' +shipping_rule.5: + rule: + enabled: true + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 +shipping_rule.6: + rule: + enabled: true + sortOrder: 2 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + country: 'FR' +shipping_rule.7: + rule: + enabled: true + sortOrder: 3 + expression: 'true' + currency: 'EUR' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_item' + price: 10 + destinations: + - + postalCodes: + - + name: '12346' + country: 'US' + region: 'NY' +shipping_rule.8: + rule: + enabled: true + sortOrder: 3 + expression: 'true' + currency: 'EUR' + destinations: + - + country: 'US' + region: 'AL' +shipping_rule.9: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'USD' + methodConfigs: + - + typeConfigs: + - + enabled: true + options: + type: 'per_order' + price: 10 +shipping_rule.10: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'UAH' +shipping_rule.11: + rule: + enabled: false + sortOrder: 0 + expression: 'true' + currency: 'UAH' +shipping_rule.12: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'UAH' + destinations: + - + country: 'US' + region: 'NY' +shipping_rule_without_type_configs: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'USD' + methodConfigs: + - + typeConfigs: [] +shipping_rule_with_disabled_type_configs: + rule: + enabled: true + sortOrder: 0 + expression: 'true' + currency: 'USD' + methodConfigs: + - + typeConfigs: + - + enabled: false + options: + type: 'per_order' + price: 10 diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodConfigRepositoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodConfigRepositoryTest.php new file mode 100644 index 000000000..206e6b72f --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodConfigRepositoryTest.php @@ -0,0 +1,90 @@ +initClient([], static::generateBasicAuthHeader()); + $this->loadFixtures([ + LoadShippingMethodTypeConfigsWithFakeTypes::class, + ]); + + $this->repository = static::getContainer()->get('doctrine') + ->getRepository('MarelloShippingBundle:ShippingMethodConfig'); + } + + public function testDeleteByMethod() + { + $method = 'ups'; + + static::assertNotEmpty($this->repository->findByMethod($method)); + + $this->repository->deleteByMethod($method); + + static::assertEmpty($this->repository->findByMethod($method)); + } + + public function testFindMethodConfigIdsWithoutTypeConfigs() + { + $methodConfig = $this->getReference('shipping_rule.3.method_config_without_type_configs'); + + static::assertEmpty($methodConfig->getTypeConfigs()); + + $ids = $this->repository->findIdsWithoutTypeConfigs(); + + static::assertEquals([$methodConfig->getId()], $ids); + } + + public function testDeleteMethodConfigByIds() + { + $ids = [ + $this->getReference('shipping_rule.3.method_config_without_type_configs')->getId(), + ]; + + $this->repository->deleteByIds($ids); + + static::assertEmpty($this->repository->findBy(['id' => $ids])); + } + + public function testFindByType() + { + $actualConfigs = $this->repository->findByMethod('flat_rate'); + + $expectedConfig = $this->getReference('shipping_rule.2.method_config.1'); + + static::assertContains($expectedConfig, $actualConfigs); + } + + public function testFindByTypes() + { + $methods = [ + 'ups', + 'flat_rate', + ]; + + $actualConfigs = $this->repository->findByMethod($methods); + + $expectedConfigs = [ + $this->getReference('shipping_rule.1.method_config.1'), + $this->getReference('shipping_rule.2.method_config.1'), + ]; + + foreach ($expectedConfigs as $expectedConfig) { + static::assertContains($expectedConfig, $actualConfigs); + } + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodTypeConfigRepositoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodTypeConfigRepositoryTest.php new file mode 100644 index 000000000..cd3ee44d5 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodTypeConfigRepositoryTest.php @@ -0,0 +1,92 @@ +initClient([], static::generateBasicAuthHeader()); + $this->loadFixtures([ + LoadShippingMethodsConfigsRulesWithConfigs::class + ]); + + $this->repository = static::getContainer()->get('doctrine') + ->getRepository('MarelloShippingBundle:ShippingMethodTypeConfig'); + } + + public function testFindShippingMethodTypeConfigConfigsByMethodAndType() + { + $ids = $this->repository->findIdsByMethodAndType( + $this->getManualShippingIdentifier(), + $this->getManualShippingPrimaryIdentifier() + ); + + static::assertContains($this->getFirstTypeId('shipping_rule.1'), $ids); + static::assertContains($this->getFirstTypeId('shipping_rule.2'), $ids); + } + + /** + * @param string $ruleReference + * @return int + */ + private function getFirstTypeId($ruleReference) + { + /** @var ShippingMethodConfig $methodConfig */ + $methodConfig = $this->getReference($ruleReference)->getMethodConfigs()->first(); + return $methodConfig->getTypeConfigs()->first()->getId(); + } + + public function testDeleteMethodConfigByIds() + { + $ids = [ + $this->getFirstTypeId('shipping_rule.1'), + $this->getFirstTypeId('shipping_rule.2'), + ]; + + static::assertCount(2, $this->repository->findBy(['id' => $ids])); + + $this->repository->deleteByIds($ids); + + static::assertEmpty($this->repository->findBy(['id' => $ids])); + } + + public function testFindEnabledByMethodIdentifier() + { + $method = $this->getManualShippingIdentifier(); + + $actual = $this->repository->findEnabledByMethodIdentifier($method); + + static::assertContains($this->getFirstType('shipping_rule.4'), $actual); + static::assertContains($this->getFirstType('shipping_rule.9'), $actual); + static::assertNotContains($this->getFirstType('shipping_rule_without_type_configs'), $actual); + static::assertNotContains($this->getFirstType('shipping_rule_with_disabled_type_configs'), $actual); + } + + /** + * @param string $ruleReference + * + * @return ShippingMethodTypeConfig + */ + private function getFirstType($ruleReference) + { + /** @var ShippingMethodConfig $methodConfig */ + $methodConfig = $this->getReference($ruleReference)->getMethodConfigs()->first(); + + return $methodConfig->getTypeConfigs()->first(); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodsConfigsRuleRepositoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodsConfigsRuleRepositoryTest.php new file mode 100644 index 000000000..54107e419 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Entity/Repository/ShippingMethodsConfigsRuleRepositoryTest.php @@ -0,0 +1,248 @@ +initClient([], static::generateBasicAuthHeader()); + $this->client->useHashNavigation(true); + + $this->loadFixtures([ + LoadShippingMethodsConfigsRulesWithConfigs::class, + ]); + + $this->em = static::getContainer()->get('doctrine') + ->getManagerForClass('MarelloShippingBundle:ShippingMethodsConfigsRule'); + $this->repository = $this->em->getRepository('MarelloShippingBundle:ShippingMethodsConfigsRule'); + } + + /** + * @param array $entities + * + * @return array + */ + private function getEntitiesIds(array $entities) + { + return array_map(function ($entity) { + return $entity->getId(); + }, $entities); + } + + /** + * @dataProvider getByDestinationAndCurrencyDataProvider + * + * @param array $shippingAddressData + * @param string $currency + * @param ShippingMethodsConfigsRule[] $expectedRules + */ + public function testGetByDestinationAndCurrency(array $shippingAddressData, $currency, array $expectedRules) + { + $expectedRulesIds = $this->getEntitiesIds($this->getEntitiesByReferences($expectedRules)); + $actualRules = $this->repository->getByDestinationAndCurrency( + $this->createShippingAddress($shippingAddressData), + $currency + ); + + $this->assertEquals($expectedRulesIds, $this->getEntitiesIds($actualRules)); + } + + /** + * @return array + */ + public function getByDestinationAndCurrencyDataProvider() + { + return [ + [ + 'shippingAddress' => [ + 'country' => 'US', + 'region' => [ + 'combinedCode' => 'US-NY', + 'code' => 'NY', + ], + 'postalCode' => '12345', + ], + 'currency' => 'EUR', + 'expectedRulesIds' => [ + 'shipping_rule.1', + 'shipping_rule.2', + 'shipping_rule.3', + 'shipping_rule.4', + 'shipping_rule.5', + ] + ], + ]; + } + + public function testGetByCurrencyWithoutDestination() + { + $currency = 'UAH'; + $expectedRules = $this->getEntitiesByReferences([ + 'shipping_rule.10', + 'shipping_rule.11' + ]); + + $actualRules = $this->repository->getByCurrencyWithoutDestination($currency); + + $this->assertEquals($this->getEntitiesIds($expectedRules), $this->getEntitiesIds($actualRules)); + } + + public function testGetRulesWithoutShippingMethods() + { + $rulesWithoutShippingMethods = $this->repository->getRulesWithoutShippingMethods(); + $enabledRulesWithoutShippingMethods = $this->repository->getRulesWithoutShippingMethods(true); + + static::assertCount(4, $rulesWithoutShippingMethods); + static::assertCount(3, $enabledRulesWithoutShippingMethods); + } + + public function testDisableRulesWithoutShippingMethods() + { + $this->repository->disableRulesWithoutShippingMethods(); + + $rulesWithoutShippingMethods = $this->repository->getRulesWithoutShippingMethods(); + $enabledRulesWithoutShippingMethods = $this->repository->getRulesWithoutShippingMethods(true); + + static::assertCount(4, $rulesWithoutShippingMethods); + static::assertCount(0, $enabledRulesWithoutShippingMethods); + } + + public function testGetRulesByMethod() + { + $rulesByExistingMethod = $this->repository->getRulesByMethod($this->getManualShippingIdentifier()); + + $expectedRuleReferences = [ + 'shipping_rule.1', + 'shipping_rule.2', + 'shipping_rule.3', + 'shipping_rule.4', + 'shipping_rule.5', + 'shipping_rule.6', + 'shipping_rule.7', + 'shipping_rule.9', + 'shipping_rule_without_type_configs', + 'shipping_rule_with_disabled_type_configs', + ]; + foreach ($expectedRuleReferences as $expectedRuleReference) { + static::assertContains($this->getReference($expectedRuleReference), $rulesByExistingMethod); + } + + $rulesByNotExistingMethod = $this->repository->getRulesByMethod('not_existing_method'); + static::assertCount(0, $rulesByNotExistingMethod); + } + + /** + * @dataProvider getEnabledRulesByMethodDataProvider + * + * @param string[] $expectedRuleReferences + */ + public function testGetEnabledRulesByMethod(array $expectedRuleReferences) + { + $actualRules = $this->repository->getEnabledRulesByMethod($this->getManualShippingIdentifier()); + + foreach ($expectedRuleReferences as $expectedRuleReference) { + static::assertContains($this->getReference($expectedRuleReference), $actualRules); + } + } + + /** + * @return array + */ + public function getEnabledRulesByMethodDataProvider() + { + return [ + [ + 'expectedRuleReferences' => [ + 'shipping_rule.1', + 'shipping_rule.2', + 'shipping_rule.4', + 'shipping_rule.5', + 'shipping_rule.6', + 'shipping_rule.7', + 'shipping_rule.9', + 'shipping_rule_without_type_configs', + 'shipping_rule_with_disabled_type_configs', + ] + ] + ]; + } + + /** + * @param array $rules + * + * @return array + */ + protected function getEntitiesByReferences(array $rules) + { + return array_map(function ($ruleReference) { + return $this->getReference($ruleReference); + }, $rules); + } + + /** + * @param array $data + * + * @return AddressInterface|object + */ + protected function createShippingAddress(array $data) + { + return $this->getEntity(ShippingAddressStub::class, [ + 'country' => new Country($data['country']), + 'region' => $this->getEntity( + Region::class, + [ + 'code' => $data['region']['code'], + ], + [ + 'combinedCode' => $data['region']['combinedCode'], + ] + ), + 'postalCode' => $data['postalCode'], + ]); + } + + public function testGetByCurrency() + { + $expectedRules = $this->getEntitiesByReferences([ + 'shipping_rule.10', + 'shipping_rule.11', + 'shipping_rule.12' + ]); + + $this->assertEquals( + $this->getEntitiesIds($expectedRules), + $this->getEntitiesIds($this->repository->getByCurrency('UAH')) + ); + } + + public function testGetByCurrencyWhenCurrencyNotExists() + { + $this->assertEmpty($this->repository->getByCurrency('WON')); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Functional/Helper/ManualShippingIntegrationTrait.php b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Helper/ManualShippingIntegrationTrait.php new file mode 100644 index 000000000..ceaf1316d --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Functional/Helper/ManualShippingIntegrationTrait.php @@ -0,0 +1,42 @@ +getChannelReference(); + + return sprintf('manual_shipping_%s', $channel->getId()); + } + + /** + * @return string + */ + protected function getManualShippingPrimaryIdentifier() + { + return 'primary'; + } + + /** + * @return Channel + */ + protected function getChannelReference() + { + return $this->getReference(LoadManualShippingIntegration::REFERENCE_MANUAL_SHIPPING); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingMethodEnabledByIdentifierCheckerTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingMethodEnabledByIdentifierCheckerTest.php new file mode 100644 index 000000000..32ebfdf1a --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingMethodEnabledByIdentifierCheckerTest.php @@ -0,0 +1,85 @@ +method = $this->createMock(ShippingMethodInterface::class); + + $this->shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + + $this->shippingMethodEnabledByIdentifierChecker = new ShippingMethodEnabledByIdentifierChecker( + $this->shippingMethodProvider + ); + } + + public function testIsEnabledForEnabledMethod() + { + $identifier = 'shipping_method_1'; + + $this->method + ->expects(static::once()) + ->method('isEnabled') + ->willReturn(true); + + $this->shippingMethodProvider + ->expects(static::any()) + ->method('getShippingMethod') + ->with($identifier) + ->willReturn($this->method); + + $this->assertTrue($this->shippingMethodEnabledByIdentifierChecker->isEnabled($identifier)); + } + + public function testIsEnabledForDisabledMethod() + { + $identifier = 'shipping_method_1'; + + $this->method + ->expects(static::once()) + ->method('isEnabled') + ->willReturn(false); + + $this->shippingMethodProvider + ->expects(static::any()) + ->method('getShippingMethod') + ->with($identifier) + ->willReturn($this->method); + + $this->assertFalse($this->shippingMethodEnabledByIdentifierChecker->isEnabled($identifier)); + } + + public function testIsEnabledForNotExistingMethod() + { + $identifier = 'shipping_method_1'; + + $this->shippingMethodProvider + ->expects(static::any()) + ->method('getShippingMethod') + ->with($identifier) + ->willReturn(null); + + $this->assertFalse($this->shippingMethodEnabledByIdentifierChecker->isEnabled($identifier)); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingRuleEnabledCheckerTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingRuleEnabledCheckerTest.php new file mode 100644 index 000000000..100460f0a --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Checker/ShippingRuleEnabledCheckerTest.php @@ -0,0 +1,63 @@ +methodEnabledChecker = $this->createMock( + ShippingMethodEnabledByIdentifierCheckerInterface::class + ); + + $this->ruleChecker = new ShippingRuleEnabledChecker($this->methodEnabledChecker); + } + + public function testCanBeEnabledForOneEnabledMethod() + { + $this->methodEnabledChecker->expects(static::at(1)) + ->method('isEnabled') + ->willReturn(true); + + $rule = $this->getRuleMock(); + + static::assertTrue($this->ruleChecker->canBeEnabled($rule)); + } + + public function testCanBeEnabledForNoEnabledMethods() + { + $rule = $this->getRuleMock(); + + static::assertFalse($this->ruleChecker->canBeEnabled($rule)); + } + + /** + * @return ShippingMethodsConfigsRule|\PHPUnit\Framework\MockObject\MockObject + */ + private function getRuleMock() + { + $rule = $this->createMock(ShippingMethodsConfigsRule::class); + $rule->expects(static::any()) + ->method('getMethodConfigs') + ->willReturn([ + new ShippingMethodConfig(), new ShippingMethodConfig() + ]); + + return $rule; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/HasApplicableShippingMethodsTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/HasApplicableShippingMethodsTest.php new file mode 100644 index 000000000..56c8103b6 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/HasApplicableShippingMethodsTest.php @@ -0,0 +1,144 @@ +shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + + $this->shippingPriceProvider = $this + ->getMockBuilder(ShippingPriceProvider::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->condition = new HasApplicableShippingMethods( + $this->shippingMethodProvider, + $this->shippingPriceProvider + ); + } + + protected function tearDown() + { + unset($this->condition, $this->shippingMethodProvider); + } + + public function testGetName() + { + $this->assertEquals(HasApplicableShippingMethods::NAME, $this->condition->getName()); + } + + /** + * @expectedException \Oro\Component\ConfigExpression\Exception\InvalidArgumentException + * @expectedExceptionMessage Missing "shippingContext" option + */ + public function testInitializeInvalid() + { + $this->assertInstanceOf( + 'Oro\Component\ConfigExpression\Condition\AbstractCondition', + $this->condition->initialize([]) + ); + } + + public function testInitialize() + { + $this->assertInstanceOf( + 'Oro\Component\ConfigExpression\Condition\AbstractCondition', + $this->condition->initialize([self::METHOD, new \stdClass()]) + ); + } + + /** + * @dataProvider evaluateProvider + * @param array $methods + * @param bool $expected + */ + public function testEvaluate($methods, $expected) + { + $method = $this->createMock('Marello\Bundle\ShippingBundle\Method\ShippingMethodInterface'); + $this->shippingMethodProvider->expects($this->any())->method('getShippingMethod')->willReturn($method); + + $this->shippingPriceProvider->expects($this->once()) + ->method('getApplicableMethodsViews') + ->willReturn($methods); + + $this->condition->initialize(['shippingContext' => new ShippingContext([])]); + $this->assertEquals($expected, $this->condition->evaluate([])); + } + + /** + * @return array + */ + public function evaluateProvider() + { + return [ + 'no_rules_no_methods' => [ + 'methods' => [], + 'expected' => false, + ], + 'with_rules_no_methods' => [ + 'methods' => [], + 'expected' => false, + ], + 'with_rules_and_methods' => [ + 'methods' => ['flat_rate'], + 'expected' => true, + ], + ]; + } + + public function testToArray() + { + $stdClass = new \stdClass(); + $this->condition->initialize(['shippingContext' => $stdClass]); + $result = $this->condition->toArray(); + + $key = '@' . HasApplicableShippingMethods::NAME; + + $this->assertInternalType('array', $result); + $this->assertArrayHasKey($key, $result); + + $resultSection = $result[$key]; + $this->assertInternalType('array', $resultSection); + $this->assertArrayHasKey('parameters', $resultSection); + $this->assertContains($stdClass, $resultSection['parameters']); + } + + public function testCompile() + { + $toStringStub = new ToStringStub(); + $options = ['shippingContext' => $toStringStub]; + + $this->condition->initialize($options); + $result = $this->condition->compile('$factory'); + $this->assertEquals( + sprintf( + '$factory->create(\'%s\', [%s])', + HasApplicableShippingMethods::NAME, + $toStringStub + ), + $result + ); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ShippingMethodHasShippingRulesTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ShippingMethodHasShippingRulesTest.php new file mode 100644 index 000000000..689a5f61a --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ShippingMethodHasShippingRulesTest.php @@ -0,0 +1,148 @@ +repository = $this->getMockBuilder(ShippingMethodsConfigsRuleRepository::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->propertyPath = $this->createMock(PropertyPathInterface::class); + $this->propertyPath->expects($this->any()) + ->method('__toString') + ->will($this->returnValue(self::PROPERTY_PATH_NAME)); + $this->propertyPath->expects($this->any()) + ->method('getElements') + ->will($this->returnValue([self::PROPERTY_PATH_NAME])); + + $this->shippingMethodHasShippingRulesCondition = new ShippingMethodHasShippingRules($this->repository); + } + + public function testGetName() + { + $this->assertEquals( + ShippingMethodHasShippingRules::NAME, + $this->shippingMethodHasShippingRulesCondition->getName() + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing "method_identifier" option + */ + public function testInitializeInvalid() + { + $this->assertInstanceOf( + ShippingMethodHasShippingRules::class, + $this->shippingMethodHasShippingRulesCondition->initialize([]) + ); + } + + public function testInitialize() + { + $this->assertInstanceOf( + ShippingMethodHasShippingRules::class, + $this->shippingMethodHasShippingRulesCondition->initialize(['method_identifier']) + ); + } + + /** + * @dataProvider evaluateProvider + * + * @param ShippingMethodsConfigsRule[] $rules + * @param bool $expected + */ + public function testEvaluate($rules, $expected) + { + $this->repository->expects(static::once()) + ->method('getRulesByMethod') + ->willReturn($rules); + + $this->shippingMethodHasShippingRulesCondition->initialize(['method_identifier']); + $this->assertEquals($expected, $this->shippingMethodHasShippingRulesCondition->evaluate([])); + } + + /** + * @return array + */ + public function evaluateProvider() + { + return [ + 'no_rules' => [ + 'rules' => [], + 'expected' => false, + ], + 'with_rules' => [ + 'rules' => [ + new ShippingMethodsConfigsRule(), + new ShippingMethodsConfigsRule(), + ], + 'expected' => true, + ], + ]; + } + + public function testToArray() + { + $result = $this->shippingMethodHasShippingRulesCondition->initialize([$this->propertyPath])->toArray(); + + $this->assertEquals( + sprintf('$%s', self::PROPERTY_PATH_NAME), + $result['@marello_shipping_method_has_shipping_rules']['parameters'][0] + ); + } + + public function testCompile() + { + $result = $this->shippingMethodHasShippingRulesCondition->compile('$factoryAccessor'); + + $this->assertContains('$factoryAccessor->create(\'marello_shipping_method_has_shipping_rules\'', $result); + } + + public function testSetContextAccessor() + { + /** @var ContextAccessorInterface|\PHPUnit\Framework\MockObject\MockObject $contextAccessor * */ + $contextAccessor = $this->getMockBuilder(ContextAccessorInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->shippingMethodHasShippingRulesCondition->setContextAccessor($contextAccessor); + + $reflection = new \ReflectionProperty( + get_class($this->shippingMethodHasShippingRulesCondition), + 'contextAccessor' + ); + $reflection->setAccessible(true); + + $this->assertInstanceOf( + get_class($contextAccessor), + $reflection->getValue($this->shippingMethodHasShippingRulesCondition) + ); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ToStringStub.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ToStringStub.php new file mode 100644 index 000000000..a2aff3af0 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Condition/ToStringStub.php @@ -0,0 +1,12 @@ +priceMock = $this->createMock(Price::class); + + $this->productHolderMock = $this->createMock(OrderItem::class); + + $this->productHolderMock->method('getId')->willReturn(static::TEST_ENTITY_ID); + + $this->productMock = $this->createMock(Product::class); + + $this->productMock->method('getSku')->willReturn(static::TEST_PRODUCT_SKU); + $this->productMock->method('getId')->willReturn(static::TEST_PRODUCT_ID); + } + + /** + * @return array + */ + protected function getShippingLineItemParams() + { + return [ + ShippingLineItem::FIELD_PRICE => $this->priceMock, + ShippingLineItem::FIELD_QUANTITY => self::TEST_QUANTITY, + ShippingLineItem::FIELD_PRODUCT_HOLDER => $this->productHolderMock, + ShippingLineItem::FIELD_PRODUCT => $this->productMock, + ShippingLineItem::FIELD_PRODUCT_SKU => self::TEST_PRODUCT_SKU, + ShippingLineItem::FIELD_WEIGHT => self::TEST_WEIGHT, + ShippingLineItem::FIELD_ENTITY_IDENTIFIER => self::TEST_ENTITY_ID, + ]; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/BasicShippingContextBuilderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/BasicShippingContextBuilderTest.php new file mode 100644 index 000000000..aa37a1356 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/BasicShippingContextBuilderTest.php @@ -0,0 +1,188 @@ +customerMock = $this->createMock(Customer::class); + $this->lineItemsCollectionMock = $this->createMock(ShippingLineItemCollectionInterface::class); + $this->billingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingOriginMock = $this->createMock(AddressInterface::class); + $this->subtotalMock = $this->createMock(Price::class); + $this->sourceEntityMock = $this->createMock(Order::class); + $this->shippingOriginMock = $this->createMock(AddressInterface::class); + } + + public function testFullContextBuilding() + { + $paymentMethod = 'paymentMethod'; + $currency = 'usd'; + $entityId = '12'; + + $builder = new BasicShippingContextBuilder( + $this->sourceEntityMock, + $entityId + ); + + $builder + ->setCurrency($currency) + ->setSubTotal($this->subtotalMock) + ->setLineItems($this->lineItemsCollectionMock) + ->setShippingAddress($this->shippingAddressMock) + ->setBillingAddress($this->billingAddressMock) + ->setCustomer($this->customerMock) + ->setPaymentMethod($paymentMethod) + ->setShippingOrigin($this->shippingOriginMock); + + $expectedContext = $this->getExpectedFullContext( + $paymentMethod, + $currency, + $entityId, + $this->shippingOriginMock + ); + $context = $builder->getResult(); + + $this->assertEquals($expectedContext, $context); + } + + public function testOptionalFields() + { + $entityId = '12'; + + $builder = new BasicShippingContextBuilder( + $this->sourceEntityMock, + $entityId + ); + $builder->setShippingOrigin($this->shippingOriginMock); + + $expectedContext = $this->getExpectedContextWithoutOptionalFields( + $entityId, + $this->shippingOriginMock + ); + + $context = $builder->getResult(); + + $this->assertEquals($expectedContext, $context); + } + + public function testWithoutOrigin() + { + $paymentMethod = 'paymentMethod'; + $currency = 'usd'; + $entityId = '12'; + + $builder = new BasicShippingContextBuilder( + $this->sourceEntityMock, + $entityId + ); + + $builder + ->setCurrency($currency) + ->setSubTotal($this->subtotalMock) + ->setLineItems($this->lineItemsCollectionMock) + ->setShippingAddress($this->shippingAddressMock) + ->setBillingAddress($this->billingAddressMock) + ->setCustomer($this->customerMock) + ->setPaymentMethod($paymentMethod); + + $expectedContext = $this->getExpectedFullContext( + $paymentMethod, + $currency, + $entityId, + null + ); + $context = $builder->getResult(); + + $this->assertEquals($expectedContext, $context); + } + + /** + * @param string $paymentMethod + * @param string $currency + * @param int $entityId + * @param AddressInterface|null $shippingOrigin + * + * @return ShippingContext + */ + private function getExpectedFullContext($paymentMethod, $currency, $entityId, AddressInterface $shippingOrigin = null) + { + $params = [ + ShippingContext::FIELD_CUSTOMER => $this->customerMock, + ShippingContext::FIELD_LINE_ITEMS => $this->lineItemsCollectionMock, + ShippingContext::FIELD_BILLING_ADDRESS => $this->billingAddressMock, + ShippingContext::FIELD_SHIPPING_ADDRESS => $this->shippingAddressMock, + ShippingContext::FIELD_SHIPPING_ORIGIN => $shippingOrigin, + ShippingContext::FIELD_PAYMENT_METHOD => $paymentMethod, + ShippingContext::FIELD_CURRENCY => $currency, + ShippingContext::FIELD_SUBTOTAL => $this->subtotalMock, + ShippingContext::FIELD_SOURCE_ENTITY => $this->sourceEntityMock, + ShippingContext::FIELD_SOURCE_ENTITY_ID => $entityId, + ]; + + return new ShippingContext($params); + } + + /** + * @param int $entityId + * @param AddressInterface $shippingOrigin + * + * @return ShippingContext + */ + private function getExpectedContextWithoutOptionalFields($entityId, AddressInterface $shippingOrigin) + { + $params = [ + ShippingContext::FIELD_LINE_ITEMS => null, + ShippingContext::FIELD_SHIPPING_ORIGIN => $shippingOrigin, + ShippingContext::FIELD_SOURCE_ENTITY => $this->sourceEntityMock, + ShippingContext::FIELD_SOURCE_ENTITY_ID => $entityId, + ]; + + return new ShippingContext($params); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicShippingContextBuilderFactoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicShippingContextBuilderFactoryTest.php new file mode 100644 index 000000000..4d6956f87 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/Builder/Basic/Factory/BasicShippingContextBuilderFactoryTest.php @@ -0,0 +1,53 @@ +lineItemsCollectionMock = $this->createMock(ShippingLineItemCollectionInterface::class); + $this->subtotalMock = $this->createMock(Price::class); + $this->sourceEntityMock = $this->createMock(Order::class); + } + + public function testCreateBuilder() + { + $entityId = '12'; + + $builderFactory = new BasicShippingContextBuilderFactory(); + + $builder = $builderFactory->createShippingContextBuilder( + $this->sourceEntityMock, + $entityId + ); + + $expectedBuilder = new BasicShippingContextBuilder( + $this->sourceEntityMock, + $entityId + ); + + $this->assertEquals($expectedBuilder, $builder); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicShippingLineItemBuilderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicShippingLineItemBuilderTest.php new file mode 100644 index 000000000..9f40a7d44 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/BasicShippingLineItemBuilderTest.php @@ -0,0 +1,48 @@ +productHolderMock + ); + + $builder + ->setProduct($this->productMock) + ->setPrice($this->priceMock) + ->setProductSku(self::TEST_PRODUCT_SKU) + ->setWeight(self::TEST_WEIGHT); + + $shippingLineItem = $builder->getResult(); + + $expectedShippingLineItem = new ShippingLineItem($this->getShippingLineItemParams()); + + $this->assertEquals($expectedShippingLineItem, $shippingLineItem); + } + + public function testOptionalBuild() + { + $builder = new BasicShippingLineItemBuilder( + self::TEST_QUANTITY, + $this->productHolderMock + ); + + $shippingLineItem = $builder->getResult(); + + $expectedShippingLineItem = new ShippingLineItem([ + ShippingLineItem::FIELD_QUANTITY => self::TEST_QUANTITY, + ShippingLineItem::FIELD_PRODUCT_HOLDER => $this->productHolderMock, + ShippingLineItem::FIELD_ENTITY_IDENTIFIER => self::TEST_ENTITY_ID + ]); + + $this->assertEquals($expectedShippingLineItem, $shippingLineItem); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicLineItemBuilderByLineItemFactoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicLineItemBuilderByLineItemFactoryTest.php new file mode 100644 index 000000000..2140fa56c --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicLineItemBuilderByLineItemFactoryTest.php @@ -0,0 +1,49 @@ +lineItemBuilderFactory = $this->createMock(ShippingLineItemBuilderFactoryInterface::class); + + $this->factory = new BasicLineItemBuilderByLineItemFactory($this->lineItemBuilderFactory); + } + + public function testCreate() + { + $lineItem = new ShippingLineItem($this->getShippingLineItemParams()); + + $builder = new BasicShippingLineItemBuilder( + $lineItem->getQuantity(), + $lineItem->getProductHolder() + ); + + $this->lineItemBuilderFactory + ->method('createBuilder') + ->willReturn($builder); + + $builder = $this->factory->createBuilder($lineItem); + + $this->assertEquals($lineItem, $builder->getResult()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicShippingLineItemBuilderFactoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicShippingLineItemBuilderFactoryTest.php new file mode 100644 index 000000000..1685b11dd --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Builder/Basic/Factory/BasicShippingLineItemBuilderFactoryTest.php @@ -0,0 +1,39 @@ +productHolderMock = $this->createMock(OrderItem::class); + } + + public function testCreate() + { + $quantity = 15; + + $builderFactory = new BasicShippingLineItemBuilderFactory(); + + $builder = $builderFactory->createBuilder( + $quantity, + $this->productHolderMock + ); + + $expectedBuilder = new BasicShippingLineItemBuilder( + $quantity, + $this->productHolderMock + ); + + $this->assertEquals($expectedBuilder, $builder); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionFactoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionFactoryTest.php new file mode 100644 index 000000000..d23a10599 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionFactoryTest.php @@ -0,0 +1,42 @@ +createShippingLineItemCollection($shippingLineItems); + + $this->assertEquals($shippingLineItems, $collection->toArray()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Expected: Marello\Bundle\ShippingBundle\Context\ShippingLineItemInterface + */ + public function testFactoryWithException() + { + $shippingLineItems = [ + new OrderItem(), + new OrderItem(), + new OrderItem(), + new OrderItem(), + ]; + + $collectionFactory = new DoctrineShippingLineItemCollectionFactory(); + $collectionFactory->createShippingLineItemCollection($shippingLineItems); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionTest.php new file mode 100644 index 000000000..500a1fe83 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/LineItem/Collection/ArrayCollectionDoctrine/DoctrineShippingLineItemCollectionTest.php @@ -0,0 +1,23 @@ +assertEquals($shippingLineItems, $collection->toArray()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextCacheKeyGeneratorTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextCacheKeyGeneratorTest.php new file mode 100644 index 000000000..fde768ff5 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextCacheKeyGeneratorTest.php @@ -0,0 +1,406 @@ +generator = new ShippingContextCacheKeyGenerator(); + } + + /** + * @param $params + * @param ShippingContext|null $context + * + * @return ShippingContext + */ + private function createContext($params, ShippingContext $context = null) + { + $actualParams = $params; + + if (null === $context) { + $actualParams[ShippingContext::FIELD_LINE_ITEMS] = new DoctrineShippingLineItemCollection([]); + } else { + $actualParams = array_merge($context->all(), $actualParams); + } + + return new ShippingContext($actualParams); + } + + /** + * @param array $lineItemsParams + * @param ShippingContext|null $context + * + * @return ShippingContext + */ + private function createContextWithLineItems(array $lineItemsParams, ShippingContext $context = null) + { + $lineItems = []; + foreach ($lineItemsParams as $params) { + $lineItems[] = new ShippingLineItem($params); + } + + return $this->createContext( + [ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection($lineItems), + ], + $context + ); + } + + public function testGenerateHashSimpleFields() + { + $context1 = $this->createContext([]); + $context2 = $this->createContext([]); + + $this->assertEquals(crc32(''), $this->generator->generateKey($context1)); + $this->assertEquals(crc32(''), $this->generator->generateKey($context2)); + + $context1 = $this->createContext([ShippingContext::FIELD_CURRENCY => 'USD'], $context1); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_CURRENCY => 'EUR'], $context2); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_CURRENCY => 'USD'], $context2); + $this->assertHashEquals($context1, $context2); + + $context1 = $this->createContext([ShippingContext::FIELD_PAYMENT_METHOD => 'payment_method'], $context1); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContext( + [ShippingContext::FIELD_PAYMENT_METHOD => 'another_payment_method'], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_PAYMENT_METHOD => 'payment_method'], $context2); + $this->assertHashEquals($context1, $context2); + + $context1 = $this->createContext([ShippingContext::FIELD_SUBTOTAL => new Price()], $context1); + $this->assertHashEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_SUBTOTAL => new Price()], $context2); + $this->assertHashEquals($context1, $context2); + + $context1 = $this->createContext([ShippingContext::FIELD_SUBTOTAL => Price::create(10, 'USD')], $context1); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_SUBTOTAL => Price::create(11, 'USD')], $context2); + $this->assertHashNotEquals($context1, $context2); + $context1 = $this->createContext([ShippingContext::FIELD_SUBTOTAL => Price::create(10, 'USD')], $context1); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_SUBTOTAL => Price::create(10, 'USD')], $context2); + $this->assertHashEquals($context1, $context2); + } + + public function testGenerateHashBillingAddress() + { + $context1 = $this->createContext([]); + $context2 = $this->createContext([]); + + $address1 = new ShippingAddressStub(); + $address2 = new ShippingAddressStub(); + + $context1 = $this->createContext([ShippingContext::FIELD_BILLING_ADDRESS => $address1], $context1); + $this->assertHashEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_BILLING_ADDRESS => $address2], $context2); + $this->assertHashEquals($context1, $context2); + + $this->assertAddressesFieldAffectsHash($context1, $context2, $address1, $address2); + } + + public function testGenerateHashShippingAddress() + { + $context1 = $this->createContext([]); + $context2 = $this->createContext([]); + + $address1 = new ShippingAddressStub(); + $address2 = new ShippingAddressStub(); + + $context1 = $this->createContext([ShippingContext::FIELD_SHIPPING_ADDRESS => $address1], $context1); + $this->assertHashEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_SHIPPING_ADDRESS => $address2], $context2); + $this->assertHashEquals($context1, $context2); + + $this->assertAddressesFieldAffectsHash($context1, $context2, $address1, $address2); + } + + public function testGenerateHashShippingOrigin() + { + $context1 = $this->createContext([]); + $context2 = $this->createContext([]); + + $address1 = new ShippingAddressStub(); + $address2 = new ShippingAddressStub(); + + $context1 = $this->createContext([ShippingContext::FIELD_SHIPPING_ORIGIN => $address1], $context1); + $this->assertHashEquals($context1, $context2); + $context2 = $this->createContext([ShippingContext::FIELD_SHIPPING_ORIGIN => $address2], $context2); + $this->assertHashEquals($context1, $context2); + + $this->assertAddressesFieldAffectsHash($context1, $context2, $address1, $address2); + } + + /** + * @param ShippingContext $context1 + * @param ShippingContext $context2 + * @param ShippingAddressStub $address1 + * @param ShippingAddressStub $address2 + */ + protected function assertAddressesFieldAffectsHash( + ShippingContext $context1, + ShippingContext $context2, + ShippingAddressStub $address1, + ShippingAddressStub $address2 + ) { + $address1->setStreet('street'); + $this->assertHashNotEquals($context1, $context2); + $address2->setStreet('another_street'); + $this->assertHashNotEquals($context1, $context2); + $address2->setStreet('street'); + $this->assertHashEquals($context1, $context2); + + $address1->setStreet2('street2'); + $this->assertHashNotEquals($context1, $context2); + $address2->setStreet2('another_street2'); + $this->assertHashNotEquals($context1, $context2); + $address2->setStreet2('street2'); + $this->assertHashEquals($context1, $context2); + + $address1->setCity('city'); + $this->assertHashNotEquals($context1, $context2); + $address2->setCity('another_city'); + $this->assertHashNotEquals($context1, $context2); + $address2->setCity('city'); + $this->assertHashEquals($context1, $context2); + + $address1->setRegionText('region'); + $this->assertHashNotEquals($context1, $context2); + $address2->setRegionText('another_region'); + $this->assertHashNotEquals($context1, $context2); + $address2->setRegionText('region'); + $this->assertHashEquals($context1, $context2); + + $address1->setRegion((new Region(1))->setCode(1)); + $this->assertHashNotEquals($context1, $context2); + $address2->setRegion((new Region(2))->setCode(2)); + $this->assertHashNotEquals($context1, $context2); + $address2->setRegion((new Region(1))->setCode(1)); + $this->assertHashEquals($context1, $context2); + + $address1->setPostalCode('postal_code'); + $this->assertHashNotEquals($context1, $context2); + $address2->setPostalCode('another_postal_code'); + $this->assertHashNotEquals($context1, $context2); + $address2->setPostalCode('postal_code'); + $this->assertHashEquals($context1, $context2); + + $country1 = new Country('postal_code'); + $country2 = new Country('postal_code'); + + $address1->setCountry($country1); + $this->assertHashNotEquals($context1, $context2); + $address2->setCountry(new Country('wrong_postal_code')); + $this->assertHashNotEquals($context1, $context2); + $address2->setCountry($country2); + $this->assertHashEquals($context1, $context2); + + $country1->setName('postal_code'); + $this->assertHashNotEquals($context1, $context2); + $country2->setName('another_postal_code'); + $this->assertHashNotEquals($context1, $context2); + $country2->setName('postal_code'); + $this->assertHashEquals($context1, $context2); + + $country1->setIso3Code('code'); + $this->assertHashNotEquals($context1, $context2); + $country2->setIso3Code('another_code'); + $this->assertHashNotEquals($context1, $context2); + $country2->setIso3Code('code'); + $this->assertHashEquals($context1, $context2); + + $address1->setOrganization('organization'); + $this->assertHashNotEquals($context1, $context2); + $address2->setOrganization('another_organization'); + $this->assertHashNotEquals($context1, $context2); + $address2->setOrganization('organization'); + $this->assertHashEquals($context1, $context2); + } + + public function testGenerateHashLineItemsOrder() + { + $context1 = $this->createContext([]); + $context2 = $this->createContext([]); + + $product1 = new Product(); + $product2 = new Product(); + + $item1 = new ShippingLineItem([ShippingLineItem::FIELD_PRODUCT => $product1]); + $item2 = new ShippingLineItem([ShippingLineItem::FIELD_PRODUCT => $product2]); + + $lineItems = new DoctrineShippingLineItemCollection([$item1, $item2]); + $context1 = $this->createContext([ShippingContext::FIELD_LINE_ITEMS => $lineItems], $context1); + $this->assertHashEquals($context1, $context2); + + $lineItems = new DoctrineShippingLineItemCollection([$item1]); + $context1 = $this->createContext([ShippingContext::FIELD_LINE_ITEMS => $lineItems], $context1); + $this->assertHashEquals($context1, $context2); + $lineItems = new DoctrineShippingLineItemCollection([$item2]); + $context2 = $this->createContext([ShippingContext::FIELD_LINE_ITEMS => $lineItems], $context2); + $this->assertHashEquals($context1, $context2); + + $item1 = new ShippingLineItem( + [ShippingLineItem::FIELD_PRODUCT => $product1, ShippingLineItem::FIELD_QUANTITY => 1] + ); + $item2 = new ShippingLineItem( + [ShippingLineItem::FIELD_PRODUCT => $product2, ShippingLineItem::FIELD_QUANTITY => 2] + ); + + $lineItems = new DoctrineShippingLineItemCollection([$item1, $item2]); + $context1 = $this->createContext([ShippingContext::FIELD_LINE_ITEMS => $lineItems], $context1); + $context2 = $this->createContext([ShippingContext::FIELD_LINE_ITEMS => $lineItems], $context2); + $this->assertHashEquals($context1, $context2); + $lineItems = new DoctrineShippingLineItemCollection([$item2, $item1]); + $context2 = $this->createContext([ShippingContext::FIELD_LINE_ITEMS => $lineItems], $context2); + $this->assertHashEquals($context1, $context2); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testGenerateHashLineItems() + { + $context1 = $this->createContext([]); + $context2 = $this->createContext([]); + + $product1 = new Product(); + $product2 = new Product(); + + $context1 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRODUCT => $product1, ShippingLineItem::FIELD_QUANTITY => 1]], + $context1 + ); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRODUCT => $product2]], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRODUCT => $product2, ShippingLineItem::FIELD_QUANTITY => 2]], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRODUCT => $product2, ShippingLineItem::FIELD_QUANTITY => 1]], + $context2 + ); + $this->assertHashEquals($context1, $context2); + + $context1 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRICE => Price::create(10, 'USD')]], + $context1 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRICE => Price::create(11, 'USD')]], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRICE => Price::create(10, 'EUR')]], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRICE => Price::create(10, 'USD')]], + $context2 + ); + $this->assertHashEquals($context1, $context2); + + $context1 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRODUCT => $this->getEntity(Product::class, ['id' => 1])]], + $context1 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRODUCT => $this->getEntity(Product::class, ['id' => 2])]], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_PRODUCT => $this->getEntity(Product::class, ['id' => 1])]], + $context2 + ); + $this->assertHashEquals($context1, $context2); + + $weight = 10; + $context1 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_WEIGHT => $weight]], + $context1 + ); + $this->assertHashNotEquals($context1, $context2); + $weight = 12; + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_WEIGHT => $weight]], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + $weight = 10; + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_WEIGHT => $weight]], + $context2 + ); + $this->assertHashEquals($context1, $context2); + + $context1 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_ENTITY_IDENTIFIER => 1]], + $context1 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_ENTITY_IDENTIFIER => 2]], + $context2 + ); + $this->assertHashNotEquals($context1, $context2); + $context2 = $this->createContextWithLineItems( + [[ShippingLineItem::FIELD_ENTITY_IDENTIFIER => 1]], + $context2 + ); + $this->assertHashEquals($context1, $context2); + } + + /** + * @param ShippingContextInterface $context1 + * @param ShippingContextInterface $context2 + */ + protected function assertHashEquals(ShippingContextInterface $context1, ShippingContextInterface $context2) + { + $this->assertEquals($this->generator->generateKey($context1), $this->generator->generateKey($context2)); + } + + /** + * @param ShippingContextInterface $context1 + * @param ShippingContextInterface $context2 + */ + protected function assertHashNotEquals(ShippingContextInterface $context1, ShippingContextInterface $context2) + { + $this->assertNotEquals($this->generator->generateKey($context1), $this->generator->generateKey($context2)); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextMockTrait.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextMockTrait.php new file mode 100644 index 000000000..cab2716f9 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextMockTrait.php @@ -0,0 +1,19 @@ +createMock(ShippingContextInterface::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextTest.php new file mode 100644 index 000000000..cb682fef3 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingContextTest.php @@ -0,0 +1,102 @@ +customerMock = $this->getMockBuilder(Customer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->lineItemsCollectionMock = $this->createMock(ShippingLineItemCollectionInterface::class); + $this->billingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingAddressMock = $this->createMock(AddressInterface::class); + $this->shippingOriginMock = $this->createMock(AddressInterface::class); + $this->subtotalMock = $this->getMockBuilder(Price::class) + ->disableOriginalConstructor() + ->getMock(); + $this->sourceEntityMock = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testConstructionAndGetters() + { + $paymentMethod = 'paymentMethod'; + $currency = 'usd'; + $entityId = '12'; + + $params = [ + ShippingContext::FIELD_CUSTOMER => $this->customerMock, + ShippingContext::FIELD_LINE_ITEMS => $this->lineItemsCollectionMock, + ShippingContext::FIELD_BILLING_ADDRESS => $this->billingAddressMock, + ShippingContext::FIELD_SHIPPING_ADDRESS => $this->shippingAddressMock, + ShippingContext::FIELD_SHIPPING_ORIGIN => $this->shippingOriginMock, + ShippingContext::FIELD_PAYMENT_METHOD => $paymentMethod, + ShippingContext::FIELD_CURRENCY => $currency, + ShippingContext::FIELD_SUBTOTAL => $this->subtotalMock, + ShippingContext::FIELD_SOURCE_ENTITY => $this->sourceEntityMock, + ShippingContext::FIELD_SOURCE_ENTITY_ID => $entityId, + ]; + + $shippingContext = new ShippingContext($params); + + $getterValues = [ + ShippingContext::FIELD_CUSTOMER => $shippingContext->getCustomer(), + ShippingContext::FIELD_LINE_ITEMS => $shippingContext->getLineItems(), + ShippingContext::FIELD_BILLING_ADDRESS => $shippingContext->getBillingAddress(), + ShippingContext::FIELD_SHIPPING_ADDRESS => $shippingContext->getShippingAddress(), + ShippingContext::FIELD_SHIPPING_ORIGIN => $shippingContext->getShippingOrigin(), + ShippingContext::FIELD_PAYMENT_METHOD => $shippingContext->getPaymentMethod(), + ShippingContext::FIELD_CURRENCY => $shippingContext->getCurrency(), + ShippingContext::FIELD_SUBTOTAL => $shippingContext->getSubtotal(), + ShippingContext::FIELD_SOURCE_ENTITY => $shippingContext->getSourceEntity(), + ShippingContext::FIELD_SOURCE_ENTITY_ID => $shippingContext->getSourceEntityIdentifier(), + ]; + + $this->assertEquals($params, $getterValues); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingLineItemTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingLineItemTest.php new file mode 100644 index 000000000..952d170f5 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Context/ShippingLineItemTest.php @@ -0,0 +1,35 @@ +getShippingLineItemParams(); + + $shippingLineItem = new ShippingLineItem($shippingLineItemParams); + + $this->assertEquals($shippingLineItemParams[ShippingLineItem::FIELD_PRICE], $shippingLineItem->getPrice()); + $this->assertEquals( + $shippingLineItemParams[ShippingLineItem::FIELD_QUANTITY], + $shippingLineItem->getQuantity() + ); + $this->assertEquals( + $shippingLineItemParams[ShippingLineItem::FIELD_PRODUCT_HOLDER], + $shippingLineItem->getProductHolder() + ); + $this->assertEquals($shippingLineItemParams[ShippingLineItem::FIELD_PRODUCT], $shippingLineItem->getProduct()); + $this->assertEquals( + $shippingLineItemParams[ShippingLineItem::FIELD_PRODUCT_SKU], + $shippingLineItem->getProductSku() + ); + $this->assertEquals($shippingLineItemParams[ShippingLineItem::FIELD_WEIGHT], $shippingLineItem->getWeight()); + $this->assertEquals( + $shippingLineItemParams[ShippingLineItem::FIELD_ENTITY_IDENTIFIER], + $shippingLineItem->getEntityIdentifier() + ); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Converter/Basic/ShippingContextToRuleValuesConverterTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Converter/Basic/ShippingContextToRuleValuesConverterTest.php new file mode 100644 index 000000000..3d484da60 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Converter/Basic/ShippingContextToRuleValuesConverterTest.php @@ -0,0 +1,105 @@ +factory = new DecoratedProductLineItemFactory( + $this->createMock(VirtualFieldsProductDecoratorFactory::class) + ); + + $this->shippingContextToRuleValuesConverter = new ShippingContextToRulesValuesConverter( + $this->factory + ); + } + + /** + * @dataProvider convertDataProvider + * @param ShippingContext $context + */ + public function testConvert(ShippingContext $context) + { + $expectedValues = [ + 'lineItems' => array_map(function (ShippingLineItem $lineItem) use ($context) { + return $this->factory + ->createLineItemWithDecoratedProductByLineItem($context->getLineItems()->toArray(), $lineItem); + }, $context->getLineItems()->toArray()), + 'shippingOrigin' => $context->getShippingOrigin(), + 'billingAddress' => $context->getBillingAddress(), + 'shippingAddress' => $context->getShippingAddress(), + 'paymentMethod' => $context->getPaymentMethod(), + 'currency' => $context->getCurrency(), + 'subtotal' => $context->getSubtotal(), + 'customer' => $context->getCustomer(), + ]; + $this->assertEquals($expectedValues, $this->shippingContextToRuleValuesConverter->convert($context)); + } + + /** + * @return array + */ + public function convertDataProvider() + { + return [ + [ + 'context' => new ShippingContext([ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection([ + new ShippingLineItem([ + ShippingLineItem::FIELD_PRODUCT => $this->getEntity(Product::class, ['id' => 1]), + ]), + ]), + ShippingContext::FIELD_SHIPPING_ORIGIN => $this->getEntity(ShippingAddressStub::class, [ + 'region' => $this->getEntity(Region::class, [ + 'code' => 'CA', + ], ['US-CA']), + ]), + ShippingContext::FIELD_BILLING_ADDRESS => $this->getEntity(ShippingAddressStub::class, [ + 'country' => new Country('US'), + ]), + ShippingContext::FIELD_SHIPPING_ADDRESS => $this->getEntity(ShippingAddressStub::class, [ + 'country' => new Country('US'), + 'region' => $this->getEntity(Region::class, [ + 'code' => 'CA', + ], ['US-CA']), + 'postalCode' => '90401', + ]), + ShippingContext::FIELD_PAYMENT_METHOD => 'integration_payment_method', + ShippingContext::FIELD_CURRENCY => 'USD', + ShippingContext::FIELD_SUBTOTAL => Price::create(10.0, 'USD'), + ShippingContext::FIELD_CUSTOMER => (new Customer())->setFirstName('Customer Name'), + ]), + ], + ]; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodConfigTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodConfigTest.php new file mode 100644 index 000000000..abf9fd46c --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodConfigTest.php @@ -0,0 +1,28 @@ + 'test']], + ['methodConfigsRule', new ShippingMethodsConfigsRule()], + ]; + + $entity = new ShippingMethodConfig(); + + $this->assertPropertyAccessors($entity, $properties); + $this->assertPropertyCollection($entity, 'typeConfigs', new ShippingMethodTypeConfig()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodTypeConfigTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodTypeConfigTest.php new file mode 100644 index 000000000..98cc54b13 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodTypeConfigTest.php @@ -0,0 +1,27 @@ + 'test']], + ['enabled', true], + ['methodConfig', new ShippingMethodConfig()], + ]; + + $entity = new ShippingMethodTypeConfig(); + + static::assertPropertyAccessors($entity, $properties); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleDestinationPostalCodeTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleDestinationPostalCodeTest.php new file mode 100644 index 000000000..0447dc321 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleDestinationPostalCodeTest.php @@ -0,0 +1,26 @@ +country = $this->createMockCountry(); + $this->region = $this->createMockRegion(); + + $this->shippingRuleDestination = $this->getEntity( + 'Marello\Bundle\ShippingBundle\Entity\ShippingMethodsConfigsRuleDestination', + [ + 'region' => $this->region, + 'country' => $this->country, + 'postalCodes' => new ArrayCollection([$this->createPostalCode('123')]), + ] + ); + } + + public function testProperties() + { + $properties = [ + ['id', 1], + ['region', new Region('code')], + ['regionText', 'text'], + ['country', new Country('UA')], + ['methodConfigsRule', new ShippingMethodsConfigsRule()], + ]; + + $destination = new ShippingMethodsConfigsRuleDestination(); + static::assertPropertyAccessors($destination, $properties); + static::assertPropertyCollection( + $destination, + 'postalCodes', + $this->createPostalCode('123') + ); + } + + public function testGetRegionName() + { + $this->assertEquals('RegionName', $this->shippingRuleDestination->getRegionName()); + $this->shippingRuleDestination->setRegion(null); + $this->assertEquals('', $this->shippingRuleDestination->getRegionName()); + } + + public function testGetRegionCode() + { + $this->assertEquals('RegionCode', $this->shippingRuleDestination->getRegionCode()); + $this->shippingRuleDestination->setRegion(null); + $this->assertEquals('', $this->shippingRuleDestination->getRegionCode()); + } + + public function testGetCountryName() + { + $this->assertEquals('CountryName', $this->shippingRuleDestination->getCountryName()); + $this->shippingRuleDestination->setCountry(null); + $this->assertEquals('', $this->shippingRuleDestination->getCountryName()); + } + + public function testGetCountryIso2() + { + $this->assertEquals('CountryIso2', $this->shippingRuleDestination->getCountryIso2()); + $this->shippingRuleDestination->setCountry(null); + $this->assertEquals('', $this->shippingRuleDestination->getCountryIso2()); + } + + public function testGetCountryIso3() + { + $this->assertEquals('CountryIso3', $this->shippingRuleDestination->getCountryIso3()); + $this->shippingRuleDestination->setCountry(null); + $this->assertEquals('', $this->shippingRuleDestination->getCountryIso3()); + } + + /** + * @dataProvider toStringDataProvider + * + * @param array $data + * @param string $expectedString + */ + public function testToString(array $data, $expectedString) + { + $entity = (string) $this->getEntity( + 'Marello\Bundle\ShippingBundle\Entity\ShippingMethodsConfigsRuleDestination', + $data + ); + $this->assertEquals($expectedString, $entity); + } + + /** + * @return array + */ + public function toStringDataProvider() + { + return [ + 'all' => [ + 'data' => [ + 'country' => $this->createMockCountry(), + 'region' => $this->createMockRegion(), + 'postalCodes' => new ArrayCollection([$this->createPostalCode('12345')]), + ], + 'expectedString' => 'RegionName, CountryName 12345' + ], + 'country and postal code' => [ + 'data' => [ + 'country' => $this->createMockCountry(), + 'region' => null, + 'postalCodes' => new ArrayCollection([ + $this->createPostalCode('12345'), + $this->createPostalCode('54321'), + ]), + ], + 'expectedString' => 'CountryName 12345, 54321' + ], + 'country and region' => [ + 'data' => [ + 'country' => $this->createMockCountry('SecondCountryName'), + 'region' => $this->createMockRegion('SecondRegionName'), + 'postalCodes' => new ArrayCollection(), + ], + 'expectedString' => 'SecondRegionName, SecondCountryName' + ], + 'only country' => [ + 'data' => [ + 'country' => $this->createMockCountry(), + 'region' => null, + 'postalCodes' => new ArrayCollection(), + ], + 'expectedString' => 'CountryName' + ] + ]; + } + + /** + * @param string $name + * @param string $iso2 + * @param string $iso3 + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function createMockCountry($name = 'CountryName', $iso2 = 'CountryIso2', $iso3 = 'CountryIso3') + { + $result = $this->getMockBuilder('Oro\Bundle\AddressBundle\Entity\Country') + ->disableOriginalConstructor() + ->getMock(); + $result->expects($this->any()) + ->method('__toString') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getIso2Code') + ->will($this->returnValue($iso2)); + $result->expects($this->any()) + ->method('getIso3Code') + ->will($this->returnValue($iso3)); + + return $result; + } + + /** + * @param string $name + * @param string $code + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function createMockRegion($name = 'RegionName', $code = 'RegionCode') + { + $result = $this->createMock('Oro\Bundle\AddressBundle\Entity\Region', [], ['combinedCode']); + $result->expects($this->any()) + ->method('__toString') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $result->expects($this->any()) + ->method('getCode') + ->will($this->returnValue($code)); + return $result; + } + + /** + * @param string $name + * @return ShippingMethodsConfigsRuleDestinationPostalCode + */ + protected function createPostalCode($name) + { + return (new ShippingMethodsConfigsRuleDestinationPostalCode())->setName($name); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleMockTrait.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleMockTrait.php new file mode 100644 index 000000000..e2d61e71e --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleMockTrait.php @@ -0,0 +1,19 @@ +createMock(ShippingMethodsConfigsRule::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleTest.php new file mode 100644 index 000000000..3571f3c54 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Entity/ShippingMethodsConfigsRuleTest.php @@ -0,0 +1,32 @@ +shippingMethodProvider = new CompositeShippingMethodProvider(); + + $this->provider = $this->getMockBuilder(ShippingMethodProviderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testGetMethods() + { + $shippingMethods = $this->shippingMethodProvider->getShippingMethods(); + $this->assertInternalType('array', $shippingMethods); + $this->assertEmpty($shippingMethods); + } + + public function testRegistry() + { + $method = $this->createMock(ShippingMethodInterface::class); + + $this->provider->expects($this->once()) + ->method('getShippingMethods') + ->willReturn(['test_name' => $method]); + + $this->provider->expects($this->once()) + ->method('getShippingMethod') + ->with('test_name') + ->willReturn($method); + + $this->provider->expects($this->once()) + ->method('hasShippingMethod') + ->with('test_name') + ->willReturn(true); + + $this->shippingMethodProvider->addProvider($this->provider); + $this->assertEquals($method, $this->shippingMethodProvider->getShippingMethod('test_name')); + $this->assertEquals(['test_name' => $method], $this->shippingMethodProvider->getShippingMethods()); + } + + public function testRegistryWrongMethod() + { + $this->assertNull($this->shippingMethodProvider->getShippingMethod('wrong_name')); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php new file mode 100644 index 000000000..77ef49394 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRemovalEventDispatcherTest.php @@ -0,0 +1,37 @@ +eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $this->dispatcher = new BasicMethodRemovalEventDispatcher($this->eventDispatcher); + } + + public function testDispatch() + { + $methodId = 'method'; + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(MethodRemovalEvent::NAME, new MethodRemovalEvent($methodId)); + + $this->dispatcher->dispatch($methodId); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php new file mode 100644 index 000000000..5d0cc13b9 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodRenamingEventDispatcherTest.php @@ -0,0 +1,38 @@ +eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $this->dispatcher = new BasicMethodRenamingEventDispatcher($this->eventDispatcher); + } + + public function testDispatch() + { + $oldId = 'old_id'; + $newId = 'new_id'; + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(MethodRenamingEvent::NAME, new MethodRenamingEvent($oldId, $newId)); + + $this->dispatcher->dispatch($oldId, $newId); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodTypeRemovalEventDispatcherTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodTypeRemovalEventDispatcherTest.php new file mode 100644 index 000000000..0a7b7222f --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/BasicMethodTypeRemovalEventDispatcherTest.php @@ -0,0 +1,38 @@ +eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $this->dispatcher = new BasicMethodTypeRemovalEventDispatcher($this->eventDispatcher); + } + + public function testDispatch() + { + $methodId = 'method'; + $typeId = 'type'; + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(MethodTypeRemovalEvent::NAME, new MethodTypeRemovalEvent($methodId, $typeId)); + + $this->dispatcher->dispatch($methodId, $typeId); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php new file mode 100644 index 000000000..d256a00d6 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRemovalEventTest.php @@ -0,0 +1,17 @@ +assertSame($methodId, $event->getMethodIdentifier()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php new file mode 100644 index 000000000..deddb5e06 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodRenamingEventTest.php @@ -0,0 +1,19 @@ +assertSame($oldId, $event->getOldMethodIdentifier()); + $this->assertSame($newId, $event->getNewMethodIdentifier()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodTypeRemovalEventTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodTypeRemovalEventTest.php new file mode 100644 index 000000000..fc17ab750 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Event/MethodTypeRemovalEventTest.php @@ -0,0 +1,19 @@ +assertSame($methodId, $event->getMethodIdentifier()); + $this->assertSame($typeId, $event->getTypeIdentifier()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php new file mode 100644 index 000000000..384af205e --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/IntegrationRemovalListenerTest.php @@ -0,0 +1,96 @@ +channelType = 'shipping_method'; + $this->identifierGenerator = $this->createMock(IntegrationIdentifierGeneratorInterface::class); + $this->dispatcher = $this->createMock(MethodRemovalEventDispatcherInterface::class); + + $this->listener = new IntegrationRemovalListener( + $this->channelType, + $this->identifierGenerator, + $this->dispatcher + ); + } + + public function testPreRemove() + { + /** @var Channel|\PHPUnit\Framework\MockObject\MockObject $channel */ + $channel = $this->createMock(Channel::class); + $channel->expects(static::once()) + ->method('getType') + ->willReturn($this->channelType); + + /** @var ChannelDeleteEvent|\PHPUnit\Framework\MockObject\MockObject $event */ + $event = $this->createMock(ChannelDeleteEvent::class); + $event->expects(static::any()) + ->method('getChannel') + ->willReturn($channel); + + $identifier = 'method'; + + $this->identifierGenerator->expects(static::once()) + ->method('generateIdentifier') + ->with($channel) + ->willReturn($identifier); + + $this->dispatcher->expects(static::once()) + ->method('dispatch') + ->with($identifier); + + $this->listener->onRemove($event); + } + + public function testPreRemoveOtherType() + { + /** @var Channel|\PHPUnit\Framework\MockObject\MockObject $channel */ + $channel = $this->createMock(Channel::class); + $channel->expects(static::once()) + ->method('getType') + ->willReturn('other_type'); + + /** @var ChannelDeleteEvent|\PHPUnit\Framework\MockObject\MockObject $event */ + $event = $this->createMock(ChannelDeleteEvent::class); + $event->expects(static::any()) + ->method('getChannel') + ->willReturn($channel); + + $this->identifierGenerator->expects(static::never()) + ->method('generateIdentifier'); + + $this->dispatcher->expects(static::never()) + ->method('dispatch'); + + $this->listener->onRemove($event); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php new file mode 100644 index 000000000..fd9691c2f --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/MethodRenamingListenerTest.php @@ -0,0 +1,59 @@ +shippingMethodConfigRepository = $this->createMock(ShippingMethodConfigRepository::class); + $this->listener = new MethodRenamingListener($this->shippingMethodConfigRepository); + } + + public function testOnMethodRename() + { + $oldId = 'old_name'; + $newId = 'new_name'; + + /** @var MethodRenamingEvent|\PHPUnit\Framework\MockObject\MockObject $event */ + $event = $this->createMock(MethodRenamingEvent::class); + $event->expects(static::any()) + ->method('getOldMethodIdentifier') + ->willReturn($oldId); + + $event->expects(static::any()) + ->method('getNewMethodIdentifier') + ->willReturn($newId); + + $config1 = $this->createMock(ShippingMethodConfig::class); + $config1->expects(static::once()) + ->method('setMethod') + ->with($newId); + $config2 = $this->createMock(ShippingMethodConfig::class); + $config2->expects(static::once()) + ->method('setMethod') + ->with($newId); + + $this->shippingMethodConfigRepository->expects(static::once()) + ->method('findByMethod') + ->with($oldId) + ->willReturn([$config1, $config2]); + + $this->listener->onMethodRename($event); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/ShippingMethodDisableIntegrationListenerTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/ShippingMethodDisableIntegrationListenerTest.php new file mode 100644 index 000000000..5fb07e858 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/EventListener/ShippingMethodDisableIntegrationListenerTest.php @@ -0,0 +1,111 @@ +channelType = 'integration_shipping_method'; + + $this->methodIdentifierGenerator = $this->createMock( + IntegrationIdentifierGeneratorInterface::class + ); + $this->handler = $this->createMock( + ShippingMethodDisableHandlerInterface::class + ); + $this->event = $this->createMock( + ChannelDisableEvent::class + ); + $this->listener = new ShippingMethodDisableIntegrationListener( + $this->channelType, + $this->methodIdentifierGenerator, + $this->handler + ); + } + + public function testOnIntegrationDisable() + { + $methodIdentifier = 'method_1'; + $channel = $this->createMock(Channel::class); + + $this->event + ->expects(static::once()) + ->method('getChannel') + ->willReturn($channel); + + $channel + ->expects(static::once()) + ->method('getType') + ->willReturn($this->channelType); + + $this->methodIdentifierGenerator + ->expects(static::once()) + ->method('generateIdentifier') + ->with($channel) + ->willReturn($methodIdentifier); + + $this->handler + ->expects(static::once()) + ->method('handleMethodDisable') + ->with($methodIdentifier); + + $this->listener->onIntegrationDisable($this->event); + } + + public function testOnIntegrationDisableWithAnotherType() + { + $channel = $this->createMock(Channel::class); + + $this->event + ->expects(static::once()) + ->method('getChannel') + ->willReturn($channel); + + $channel + ->expects(static::once()) + ->method('getType') + ->willReturn('another_type'); + + $this->methodIdentifierGenerator + ->expects(static::never()) + ->method('generateIdentifier'); + + $this->handler + ->expects(static::never()) + ->method('handleMethodDisable'); + + $this->listener->onIntegrationDisable($this->event); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Handler/RulesShippingMethodDisableHandlerDecoratorTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Handler/RulesShippingMethodDisableHandlerDecoratorTest.php new file mode 100644 index 000000000..f3a46bda1 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Handler/RulesShippingMethodDisableHandlerDecoratorTest.php @@ -0,0 +1,151 @@ +handler = $this->createMock(ShippingMethodDisableHandlerInterface::class); + $this->repository = $this->createMock(ShippingMethodsConfigsRuleRepository::class); + $this->shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + + $this->decorator = new RulesShippingMethodDisableHandlerDecorator( + $this->handler, + $this->repository, + $this->shippingMethodProvider + ); + } + + /** + * @param string $disabledMethodId + * @param array $configs + * @param array $registryMap + * + * @dataProvider testHandleMethodDisableProvider + */ + public function testHandleMethodDisable($disabledMethodId, $configs, $registryMap) + { + $this->handler->expects(self::once())->method('handleMethodDisable')->with($disabledMethodId); + + $configMocks = []; + $registryMapValues = []; + $methods = []; + foreach ($registryMap as $methodId => $enabled) { + $methods[$methodId] = $this->createMock(ShippingMethodInterface::class); + $methods[$methodId]->expects(self::any())->method('isEnabled')->willReturn($enabled); + $registryMapValues[] = [$methodId, $methods[$methodId]]; + } + + $rules = []; + foreach ($configs as $configName => $config) { + $methodConfigs = []; + foreach ($config['methods'] as $methodId) { + $methodConfig = $this->createMock(ShippingMethodConfig::class); + $methodConfig->expects(self::once())->method('getMethod')->willReturn($methodId); + $methodConfigs[] = $methodConfig; + } + $rules[$configName] = $this->createMock(Rule::class); + $rules[$configName]->expects(self::exactly($config['rule_disabled']))->method('setEnabled')->with(false); + + $configMock = $this->createMock(ShippingMethodsConfigsRule::class); + $configMock->expects(self::once()) + ->method('getMethodConfigs') + ->willReturn($methodConfigs); + $configMock->expects(self::any()) + ->method('getRule') + ->willReturn($rules[$configName]); + $configMocks[] = $configMock; + } + + $this->shippingMethodProvider + ->method('getShippingMethod') + ->will($this->returnValueMap($registryMapValues)); + + $this->repository->expects(self::once()) + ->method('getEnabledRulesByMethod') + ->willReturn($configMocks); + + $this->decorator->handleMethodDisable($disabledMethodId); + } + + /** + * @return array + */ + public function testHandleMethodDisableProvider() + { + return [ + 'a_few_methods' => + [ + 'methodId' => 'method1', + 'configs' => + [ + 'config1' => + [ + 'methods' => ['method1', 'method2'], + 'rule_disabled' => 1, + ], + 'config2' => + [ + 'methods' => ['method1', 'method3'], + 'rule_disabled' => 0, + ] + ], + 'registry_map' => + [ + 'method1' => true, + 'method2' => false, + 'method3' => true, + ], + ], + 'only_method' => + [ + 'methodId' => 'method1', + 'configs' => + [ + 'config1' => + [ + 'methods' => ['method1'], + 'rule_disabled' => 1, + ], + ], + 'registry_map' => + [ + 'method1' => true, + ], + ], + ]; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Integration/ChannelShippingMethodProviderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Integration/ChannelShippingMethodProviderTest.php new file mode 100644 index 000000000..b8b7b9012 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Integration/ChannelShippingMethodProviderTest.php @@ -0,0 +1,136 @@ +doctrineHelper = $this->createMock(DoctrineHelper::class); + $repository = $this->createMock(ChannelRepository::class); + + $this->doctrineHelper + ->method('getEntityRepository') + ->with('OroIntegrationBundle:Channel') + ->willReturn($repository); + + $loadedChannel = $this->createChannel('ch_enabled'); + $fetchedChannel = $this->createChannel('ch_disabled'); + + $this->enabledMethod = $this->createMock(ShippingMethodInterface::class); + $this->enabledMethod + ->method('getIdentifier') + ->willReturn('ups_10'); + + $this->disabledMethod = $this->createMock(ShippingMethodInterface::class); + $this->disabledMethod + ->method('getIdentifier') + ->willReturn('ups_20'); + + $this->methodFactory = $this->createMock(IntegrationShippingMethodFactoryInterface::class); + $this->methodFactory + ->method('create') + ->will($this->returnValueMap([ + [$loadedChannel, $this->enabledMethod], + [$fetchedChannel, $this->disabledMethod], + ])); + + $this->provider = new ChannelShippingMethodProvider(static::TYPE, $this->doctrineHelper, $this->methodFactory); + + $doctrineEvent = $this->createLifecycleEventArgsMock(); + $this->provider->postLoad($loadedChannel, $doctrineEvent); + + $repository + ->method('findByTypeAndExclude') + ->will(static::returnCallback(function () use ($fetchedChannel, $doctrineEvent) { + $this->provider->postLoad($fetchedChannel, $doctrineEvent); + return [$fetchedChannel]; + })); + } + + public function testGetShippingMethods() + { + $methods = $this->provider->getShippingMethods(); + static::assertCount(2, $methods); + $actualMethod = reset($methods); + static::assertSame($this->enabledMethod, $actualMethod); + } + + public function testGetShippingMethod() + { + $method = $this->provider->getShippingMethod($this->enabledMethod->getIdentifier()); + static::assertInstanceOf(ShippingMethodInterface::class, $method); + } + + public function testHasShippingMethod() + { + static::assertTrue($this->provider->hasShippingMethod($this->enabledMethod->getIdentifier())); + } + + public function testHasShippingMethodFalse() + { + static::assertFalse($this->provider->hasShippingMethod('wrong')); + } + + /** + * @param string $name + * + * @return Channel + */ + private function createChannel($name) + { + return $this->getEntity( + Channel::class, + ['id' => 20, 'name' => $name, 'type' => static::TYPE] + ); + } + + /** + * @return LifecycleEventArgs|\PHPUnit\Framework\MockObject\MockObject + */ + private function createLifecycleEventArgsMock() + { + return $this->createMock(LifecycleEventArgs::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Label/Type/BasicMethodTypeLabelsProviderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Label/Type/BasicMethodTypeLabelsProviderTest.php new file mode 100644 index 000000000..700fdaf14 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Label/Type/BasicMethodTypeLabelsProviderTest.php @@ -0,0 +1,110 @@ +methodProvider = $this->createMock(ShippingMethodProviderInterface::class); + + $this->provider = new BasicMethodTypeLabelsProvider($this->methodProvider); + } + + public function testGetLabels() + { + $methodId = 'method_id'; + $typeId1 = 'type_id_1'; + $typeId2 = 'type_id_2'; + + $label1 = 'Label 1'; + $label2 = 'Label 2'; + + $type1 = $this->createMock(ShippingMethodTypeInterface::class); + $type1->expects(static::once()) + ->method('getLabel') + ->willReturn($label1); + + $type2 = $this->createMock(ShippingMethodTypeInterface::class); + $type2->expects(static::once()) + ->method('getLabel') + ->willReturn($label2); + + $method = $this->createMock(ShippingMethodInterface::class); + $method->expects(static::at(0)) + ->method('getType') + ->with($typeId1) + ->willReturn($type1); + + $method->expects(static::at(1)) + ->method('getType') + ->with($typeId2) + ->willReturn($type2); + + $this->methodProvider->expects(static::once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn($method); + + $this->provider->getLabels($methodId, [$typeId1, $typeId2]); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Shipping method with identifier: method_id, does not exist. + */ + public function testGetLabelsNoMethod() + { + $methodId = 'method_id'; + + $this->methodProvider->expects(static::once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn(null); + + $this->provider->getLabels($methodId, []); + } + + public function testGetLabelsNoType() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Shipping method with identifier: method_id does not contain type with identifier: type_id.' + ); + + $methodId = 'method_id'; + $typeId = 'type_id'; + + $method = $this->createMock(ShippingMethodInterface::class); + $method->expects(static::once()) + ->method('getType') + ->with($typeId) + ->willReturn(null); + + $this->methodProvider->expects(static::once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn($method); + + $this->provider->getLabels($methodId, [$typeId]); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Type/NonDeletable/ShippingRulesNonDeletableMethodTypeIdentifiersProviderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Type/NonDeletable/ShippingRulesNonDeletableMethodTypeIdentifiersProviderTest.php new file mode 100644 index 000000000..9f358d3ae --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Provider/Type/NonDeletable/ShippingRulesNonDeletableMethodTypeIdentifiersProviderTest.php @@ -0,0 +1,87 @@ +methodTypeConfigRepository = $this->createMock(ShippingMethodTypeConfigRepository::class); + + $this->provider = new ShippingRulesNonDeletableMethodTypeIdentifiersProvider($this->methodTypeConfigRepository); + } + + public function testGetMethodTypeIdentifiers() + { + $typeId1 = 'type_1'; + $typeId2 = 'type_2'; + $disabledTypeId = 'disabled_type'; + + $type1 = $this->createMock(ShippingMethodTypeInterface::class); + $type1->expects(static::once()) + ->method('getIdentifier') + ->willReturn($typeId1); + + $type2 = $this->createMock(ShippingMethodTypeInterface::class); + $type2->expects(static::once()) + ->method('getIdentifier') + ->willReturn($typeId2); + + $methodId = 'method_id'; + $shippingMethod = $this->createMethodMock(); + $shippingMethod->expects(static::once()) + ->method('getIdentifier') + ->willReturn($methodId); + + $shippingMethod->expects(static::once()) + ->method('getTypes') + ->willReturn([$type1, $type2]); + + $methodTypeConfig1 = $this->createMock(ShippingMethodTypeConfig::class); + $methodTypeConfig1->expects(static::once()) + ->method('getType') + ->willReturn($typeId1); + + $methodTypeConfig2 = $this->createMock(ShippingMethodTypeConfig::class); + $methodTypeConfig2->expects(static::once()) + ->method('getType') + ->willReturn($disabledTypeId); + + $this->methodTypeConfigRepository->expects(static::once()) + ->method('findEnabledByMethodIdentifier') + ->with($methodId) + ->willReturn([$methodTypeConfig1, $methodTypeConfig2]); + + $actualNonDeletableTypeIds = $this->provider->getMethodTypeIdentifiers($shippingMethod); + + static::assertCount(1, $actualNonDeletableTypeIds); + static::assertContains($disabledTypeId, $actualNonDeletableTypeIds); + } + + /** + * @return ShippingMethodInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createMethodMock() + { + return $this->createMock(ShippingMethodInterface::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewCollectionTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewCollectionTest.php new file mode 100644 index 000000000..5b648ddaf --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewCollectionTest.php @@ -0,0 +1,599 @@ +createCollection(); + + $methodId = 'someMethodId'; + + $view = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $addResult = $collection->addMethodView($methodId, $view); + + $actualView = $collection->getMethodView($methodId); + + $this->assertEquals($collection, $addResult); + $this->assertNotNull($actualView); + $this->assertEquals($view, $actualView); + } + + public function testGetMethodViewWhenNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + + $actualView = $collection->getMethodView($methodId); + + $this->assertNull($actualView); + } + + public function testAddMethodViewWhenAlreadyExists() + { + $methodId = 'someMethodId'; + + $collection = $this->createCollection(); + + $view = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $view); + + $view2 = [ + 'someField3' => 'someValue4', + 'someField4' => 'someValue4', + 'sortOrder' => 1 + ]; + + $addMethodViewResult = $collection->addMethodView($methodId, $view2); + + $actualView = $collection->getMethodView($methodId); + + $this->assertNotNull($actualView); + $this->assertEquals($view, $actualView); + $this->assertEquals($collection, $addMethodViewResult); + } + + public function testHasMethodView() + { + $methodId = 'someMethodId'; + $collection = $this->createCollection(); + + $collection->addMethodView($methodId, []); + + $this->assertTrue($collection->hasMethodView($methodId)); + } + + public function testHasMethodViewNotExists() + { + $collection = $this->createCollection(); + + $this->assertFalse($collection->hasMethodView('someMethodId')); + } + + public function testRemoveMethodView() + { + $methodId = 'someMethodId'; + $collection = $this->createCollection(); + + $collection->addMethodView($methodId, []); + + $this->assertTrue($collection->hasMethodView($methodId)); + + $removeResult = $collection->removeMethodView($methodId); + + $this->assertEquals($collection, $removeResult); + $this->assertFalse($collection->hasMethodView($methodId)); + } + + public function testRemoveMethodViewWhenNotExists() + { + $methodId = 'someMethodId'; + $collection = $this->createCollection(); + + $removeResult = $collection->removeMethodView($methodId); + + $this->assertEquals($collection, $removeResult); + $this->assertFalse($collection->hasMethodView($methodId)); + } + + public function testAddAndGetMethodTypeView() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $methodTypeView = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $addMethodTypeViewResult = $collection->addMethodTypeView($methodId, $methodTypeId, $methodTypeView); + + $actualMethodTypeView = $collection->getMethodTypeView($methodId, $methodTypeId); + + $this->assertEquals($collection, $addMethodTypeViewResult); + $this->assertEquals($methodTypeView, $actualMethodTypeView); + } + + public function testGetMethodTypeViewWhenNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $collection->addMethodView($methodId, []); + + $actualMethodTypeView = $collection->getMethodTypeView($methodId, $methodTypeId); + + $this->assertNull($actualMethodTypeView); + } + + public function testGetMethodTypeViewWhenMethodTypeNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $actualMethodTypeView = $collection->getMethodTypeView($methodId, $methodTypeId); + + $this->assertNull($actualMethodTypeView); + } + + public function testAddMethodTypeViewWhenAlreadyExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $methodTypeView1 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId, $methodTypeView1); + + $methodTypeView2 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId, $methodTypeView2); + + $actualMethodTypeView = $collection->getMethodTypeView($methodId, $methodTypeId); + $this->assertEquals($methodTypeView1, $actualMethodTypeView); + } + + public function testAddMethodTypesViews() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $methodTypeId = 'someMethodTypeId'; + + $methodTypeView1 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId, $methodTypeView1); + + $methodTypeId2 = 'someOtherMethodTypeId'; + + $methodTypeView2 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId2, $methodTypeView2); + + $this->assertEquals($methodTypeView1, $collection->getMethodTypeView($methodId, $methodTypeId)); + $this->assertEquals($methodTypeView2, $collection->getMethodTypeView($methodId, $methodTypeId2)); + } + + public function testHasMethodTypeView() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $collection->addMethodView($methodId, []); + $collection->addMethodTypeView($methodId, $methodTypeId, []); + + $this->assertTrue($collection->hasMethodTypeView($methodId, $methodTypeId)); + } + + public function testHasMethodTypeViewWhenNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $collection->addMethodView($methodId, []); + + $this->assertFalse($collection->hasMethodTypeView($methodId, $methodTypeId)); + } + + public function testHasMethodTypeViewWhenMethodNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $this->assertFalse($collection->hasMethodTypeView($methodId, $methodTypeId)); + } + + public function testRemoveMethodTypeView() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $collection->addMethodView($methodId, []); + $collection->addMethodTypeView($methodId, $methodTypeId, []); + + $this->assertTrue($collection->hasMethodTypeView($methodId, $methodTypeId)); + + $removeResult = $collection->removeMethodTypeView($methodId, $methodTypeId); + + $this->assertEquals($collection, $removeResult); + $this->assertNull($collection->getMethodTypeView($methodId, $methodTypeId)); + } + + public function testRemoveMethodTypeViewWhenNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $collection->addMethodView($methodId, []); + + $this->assertFalse($collection->hasMethodTypeView($methodId, $methodTypeId)); + + $removeResult = $collection->removeMethodTypeView($methodId, $methodTypeId); + + $this->assertEquals($collection, $removeResult); + $this->assertNull($collection->getMethodTypeView($methodId, $methodTypeId)); + } + + public function testRemoveMethodTypeViewWhenMethodNotExists() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + + $this->assertFalse($collection->hasMethodTypeView($methodId, $methodTypeId)); + + $removeResult = $collection->removeMethodTypeView($methodId, $methodTypeId); + + $this->assertEquals($collection, $removeResult); + $this->assertNull($collection->getMethodTypeView($methodId, $methodTypeId)); + } + + public function testGetAllMethodsViews() + { + $collection = $this->createCollection(); + + $this->assertEquals([], $collection->getAllMethodsViews()); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $methodTypeId = 'someMethodTypeId'; + + $methodTypeView1 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId, $methodTypeView1); + + $methodTypeId2 = 'someMethodTypeId2'; + + $methodTypeView2 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId2, $methodTypeView2); + + $this->assertEquals([$methodId => $methodView], $collection->getAllMethodsViews()); + + $methodId2 = 'someOtherMethodId'; + + $methodView2 = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId2, $methodView2); + + $methodTypeId3 = 'someMethodTypeId3'; + + $methodTypeView3 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId2, $methodTypeId3, $methodTypeView3); + + $methodTypeId4 = 'someMethodTypeId4'; + + $methodTypeView4 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId2, $methodTypeId4, $methodTypeView4); + + $this->assertEquals([$methodId => $methodView, $methodId2 => $methodView2], $collection->getAllMethodsViews()); + } + + public function testGetAllMethodsTypesViews() + { + $collection = $this->createCollection(); + + $this->assertEquals([], $collection->getAllMethodsViews()); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $methodTypeId = 'someMethodTypeId'; + + $methodTypeView1 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId, $methodTypeView1); + + $methodTypeId2 = 'someMethodTypeId2'; + + $methodTypeView2 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId2, $methodTypeView2); + + $this->assertEquals( + [ + $methodId => [ + $methodTypeId => $methodTypeView1, + $methodTypeId2 => $methodTypeView2, + ], + ], + $collection->getAllMethodsTypesViews() + ); + + $methodId2 = 'someOtherMethodId'; + + $methodView2 = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId2, $methodView2); + + $methodTypeId3 = 'someMethodTypeId3'; + + $methodTypeView3 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId2, $methodTypeId3, $methodTypeView3); + + $methodTypeId4 = 'someMethodTypeId4'; + + $methodTypeView4 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId2, $methodTypeId4, $methodTypeView4); + + $this->assertEquals( + [ + $methodId => [ + $methodTypeId => $methodTypeView1, + $methodTypeId2 => $methodTypeView2, + ], + $methodId2 => [ + $methodTypeId3 => $methodTypeView3, + $methodTypeId4 => $methodTypeView4, + ], + ], + $collection->getAllMethodsTypesViews() + ); + + $collection->clear(); + + $this->assertEquals([], $collection->getAllMethodsTypesViews()); + $this->assertEquals([], $collection->getAllMethodsViews()); + } + + public function testToArray() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId, $methodView); + + $methodTypeId = 'someMethodTypeId'; + + $methodTypeView1 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId, $methodTypeView1); + + $methodTypeId2 = 'someMethodTypeId2'; + + $methodTypeView2 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId, $methodTypeId2, $methodTypeView2); + + $methodId2 = 'someOtherMethodId'; + + $methodView2 = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + + $collection->addMethodView($methodId2, $methodView2); + + $methodTypeId3 = 'someMethodTypeId3'; + + $methodTypeView3 = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $collection->addMethodTypeView($methodId2, $methodTypeId3, $methodTypeView3); + + $methodTypeId4 = 'someMethodTypeId4'; + + $methodTypeView4 = [ + 'someTypeField3' => 'someTypeValue3', + 'someTypeField4' => 'someTypeValue4', + ]; + + $collection->addMethodTypeView($methodId2, $methodTypeId4, $methodTypeView4); + + $methodView[ShippingMethodViewCollection::TYPES_FIELD] = [ + $methodTypeId => $methodTypeView1, + $methodTypeId2 => $methodTypeView2, + ]; + $methodView2[ShippingMethodViewCollection::TYPES_FIELD] = [ + $methodTypeId3 => $methodTypeView3, + $methodTypeId4 => $methodTypeView4, + ]; + + $this->assertEquals( + [ + $methodId => $methodView, + $methodId2 => $methodView2 + ], + $collection->toArray() + ); + } + + public function testIsEmpty() + { + $collection = $this->createCollection(); + + $methodId = 'someMethodId'; + + $methodView = [ + 'someField1' => 'someValue1', + 'someField2' => 'someValue2', + 'sortOrder' => 1 + ]; + $methodTypeView = [ + 'someTypeField1' => 'someTypeValue1', + 'someTypeField2' => 'someTypeValue2', + ]; + + $this->assertTrue($collection->isEmpty()); + + $collection->addMethodView($methodId, $methodView); + + $collection->addMethodTypeView($methodId, 'someMethodTypeId', $methodTypeView); + + $this->assertFalse($collection->isEmpty()); + + $collection->clear(); + + $this->assertTrue($collection->isEmpty()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewFactoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewFactoryTest.php new file mode 100644 index 000000000..254ab5da7 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/ShippingMethodViewFactoryTest.php @@ -0,0 +1,225 @@ +shippingMethodProviderMock = $this + ->getMockBuilder(ShippingMethodProviderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->shippingMethodViewFactory = new ShippingMethodViewFactory($this->shippingMethodProviderMock); + } + + public function testCreateMethodView() + { + $methodId = 'someId'; + $isGrouped = true; + $label = 'someLabel'; + $sortOrder = 5; + + $expected = [ + 'identifier' => $methodId, + 'isGrouped' => $isGrouped, + 'label' => $label, + 'sortOrder' => $sortOrder, + ]; + + $actual = $this->shippingMethodViewFactory->createMethodView($methodId, $label, $isGrouped, $sortOrder); + + $this->assertEquals($expected, $actual); + } + + public function testCreateMethodViewTest() + { + $methodId = 'someId'; + $label = 'someLabel'; + $sortOrder = 5; + $price = Price::create(5, 'USD'); + + $expected = [ + 'identifier' => $methodId, + 'label' => $label, + 'sortOrder' => $sortOrder, + 'price' => $price, + ]; + + $actual = $this->shippingMethodViewFactory->createMethodTypeView($methodId, $label, $sortOrder, $price); + + $this->assertEquals($expected, $actual); + } + + public function testCreateMethodViewByShippingMethod() + { + $methodId = 'someId'; + $isGrouped = true; + $label = 'someLabel'; + $sortOrder = 5; + + $methodMock = $this->getMockBuilder(ShippingMethodInterface::class)->getMock(); + + $methodMock + ->expects($this->once()) + ->method('getLabel') + ->willReturn($label); + + $methodMock + ->expects($this->once()) + ->method('isGrouped') + ->willReturn($isGrouped); + + $methodMock + ->expects($this->once()) + ->method('getSortOrder') + ->willReturn($sortOrder); + + $this->shippingMethodProviderMock + ->expects($this->once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn($methodMock); + + $expected = [ + 'identifier' => $methodId, + 'isGrouped' => $isGrouped, + 'label' => $label, + 'sortOrder' => $sortOrder, + ]; + + $actual = $this->shippingMethodViewFactory->createMethodViewByShippingMethod($methodId); + + $this->assertEquals($expected, $actual); + } + + public function createMethodViewByShippingMethodWithNullMethod() + { + $methodId = 'someMethodId'; + + $this->shippingMethodProviderMock + ->expects($this->once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn(null); + + $actual = $this->shippingMethodViewFactory->createMethodViewByShippingMethod($methodId); + + $this->assertEquals(null, $actual); + } + + public function testCreateMethodTypeViewByShippingMethodAndPrice() + { + $methodId = 'someId'; + $methodTypeId = 'someMethodTypeId'; + $label = 'someLabel'; + $sortOrder = 5; + $price = Price::create(5, 'USD'); + + $methodTypeMock = $this->getMockBuilder(ShippingMethodTypeInterface::class)->getMock(); + + $methodTypeMock + ->expects($this->once()) + ->method('getLabel') + ->willReturn($label); + + $methodTypeMock + ->expects($this->once()) + ->method('getSortOrder') + ->willReturn($sortOrder); + + $methodMock = $this->getMockBuilder(ShippingMethodInterface::class)->getMock(); + + $methodMock + ->expects($this->once()) + ->method('getType') + ->willReturn($methodTypeMock); + + $this->shippingMethodProviderMock + ->expects($this->once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn($methodMock); + + $expected = [ + 'identifier' => $methodTypeId, + 'label' => $label, + 'sortOrder' => $sortOrder, + 'price' => $price, + ]; + + $actual = $this->shippingMethodViewFactory->createMethodTypeViewByShippingMethodAndPrice( + $methodId, + $methodTypeId, + $price + ); + + $this->assertEquals($expected, $actual); + } + + public function testCreateMethodTypeViewByShippingMethodAndPriceWithNullMethod() + { + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + $price = Price::create(5, 'USD'); + + $this->shippingMethodProviderMock + ->expects($this->once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn(null); + + $actual = $this->shippingMethodViewFactory->createMethodTypeViewByShippingMethodAndPrice( + $methodId, + $methodTypeId, + $price + ); + + $this->assertEquals(null, $actual); + } + + public function testCreateMethodTypeViewByShippingMethodAndPriceWithNullMethodType() + { + $methodId = 'someMethodId'; + $methodTypeId = 'someMethodTypeId'; + $price = Price::create(5, 'USD'); + + $methodMock = $this->getMockBuilder(ShippingMethodInterface::class)->getMock(); + + $methodMock + ->expects($this->once()) + ->method('getType') + ->willReturn(null); + + $this->shippingMethodProviderMock + ->expects($this->once()) + ->method('getShippingMethod') + ->with($methodId) + ->willReturn($methodMock); + + $actual = $this->shippingMethodViewFactory->createMethodTypeViewByShippingMethodAndPrice( + $methodId, + $methodTypeId, + $price + ); + + $this->assertEquals(null, $actual); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Stub/TrackingAwareShippingMethodStub.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Stub/TrackingAwareShippingMethodStub.php new file mode 100644 index 000000000..73f1ce9f2 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Stub/TrackingAwareShippingMethodStub.php @@ -0,0 +1,81 @@ +shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + $this->trackingAwareShippingMethodsProvider = + new TrackingAwareShippingMethodsProvider($this->shippingMethodProvider); + } + + /** + * @dataProvider getTrackingAwareShippingMethodsProvider + * + * @param array $methods + * @param int $trackingAwareCount + */ + public function testGetTrackingAwareShippingMethods(array $methods, $trackingAwareCount) + { + $this->shippingMethodProvider->expects(static::once()) + ->method('getShippingMethods') + ->willReturn($methods); + + static::assertCount( + $trackingAwareCount, + $this->trackingAwareShippingMethodsProvider->getTrackingAwareShippingMethods() + ); + } + + /** + * @return array + */ + public function getTrackingAwareShippingMethodsProvider() + { + return [ + [ + 'methods' => [ + $this->mockShippingMethod(ShippingMethodInterface::class, 'method1'), + $this->mockShippingMethod(ShippingMethodInterface::class, 'method2'), + $this->mockShippingMethod(TrackingAwareShippingMethodStub::class, 'method3') + ], + 'trackingAwareCount' => 1, + ], + [ + 'methods' => [ + $this->mockShippingMethod(ShippingMethodInterface::class, 'method1'), + $this->mockShippingMethod(ShippingMethodInterface::class, 'method2'), + $this->mockShippingMethod(TrackingAwareShippingMethodStub::class, 'method3'), + $this->mockShippingMethod(TrackingAwareShippingMethodStub::class, 'method4') + ], + 'trackingAwareCount' => 2, + ], + [ + 'methods' => [ + $this->mockShippingMethod(ShippingMethodInterface::class, 'method1'), + $this->mockShippingMethod(ShippingMethodInterface::class, 'method2'), + $this->mockShippingMethod(TrackingAwareShippingMethodStub::class, 'method3'), + $this->mockShippingMethod(TrackingAwareShippingMethodStub::class, 'method4'), + $this->mockShippingMethod(TrackingAwareShippingMethodStub::class, 'method5'), + + ], + 'trackingAwareCount' => 3, + ] + + ]; + } + + /** + * @param string $class + * @param string $identifier + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function mockShippingMethod($class, $identifier) + { + $method = $this->createMock($class); + $method->expects(static::any()) + ->method('getIdentifier') + ->willReturn($identifier); + + return $method; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Basic/BasicShippingMethodValidatorTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Basic/BasicShippingMethodValidatorTest.php new file mode 100644 index 000000000..cbea426e3 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Basic/BasicShippingMethodValidatorTest.php @@ -0,0 +1,47 @@ +commonShippingMethodValidatorResultFactory = $this->createMock( + Common\CommonShippingMethodValidatorResultFactoryInterface::class + ); + + $this->validator = new BasicShippingMethodValidator($this->commonShippingMethodValidatorResultFactory); + } + + public function testValidate() + { + /** @var ShippingMethodInterface $shippingMethod */ + $shippingMethod = $this->createMock(ShippingMethodInterface::class); + + $result = $this->createMock(ShippingMethodValidatorResultInterface::class); + + $this->commonShippingMethodValidatorResultFactory->expects(static::once()) + ->method('createSuccessResult') + ->willReturn($result); + + static::assertSame($result, $this->validator->validate($shippingMethod)); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/EnabledShippingMethodsByRules/EnabledShippingMethodsByRulesShippingMethodValidatorDecoratorTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/EnabledShippingMethodsByRules/EnabledShippingMethodsByRulesShippingMethodValidatorDecoratorTest.php new file mode 100644 index 000000000..8007e9a13 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/EnabledShippingMethodsByRules/EnabledShippingMethodsByRulesShippingMethodValidatorDecoratorTest.php @@ -0,0 +1,307 @@ +parentShippingMethodValidator = $this->createMock(ShippingMethodValidatorInterface::class); + $this->errorFactory = $this->createMock( + Common\CommonShippingMethodValidatorResultErrorFactoryInterface::class + ); + $this->nonDeletableTypeIdentifiersProvider = $this->createMock( + NonDeletableMethodTypeIdentifiersProviderInterface::class + ); + $this->methodTypeLabelsProvider = $this->createMock(MethodTypeLabelsProviderInterface::class); + $this->translator = $this->createMock(TranslatorInterface::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->validator = + new Validator\EnabledShippingMethodsByRules\EnabledShippingMethodsByRulesShippingMethodValidatorDecorator( + $this->parentShippingMethodValidator, + $this->errorFactory, + $this->nonDeletableTypeIdentifiersProvider, + $this->methodTypeLabelsProvider, + $this->translator, + $this->logger + ); + } + + public function testValidateNoIdentifier() + { + $method = $this->getShippingMethodMock(); + + $result = $this->createMock(ShippingMethodValidatorResultInterface::class); + + $this->parentShippingMethodValidator->expects(static::once()) + ->method('validate') + ->with($method) + ->willReturn($result); + + $this->nonDeletableTypeIdentifiersProvider->expects(static::once()) + ->method('getMethodTypeIdentifiers') + ->with($method) + ->willReturn([]); + + static::assertSame($result, $this->validator->validate($method)); + } + + public function testValidateLabelException() + { + $methodId = 'method_1'; + + $method = $this->getShippingMethodMock(); + $method->expects(static::once()) + ->method('getIdentifier') + ->willReturn($methodId); + + $result = $this->createMock(ShippingMethodValidatorResultInterface::class); + + $this->parentShippingMethodValidator->expects(static::once()) + ->method('validate') + ->with($method) + ->willReturn($result); + + $typeIdentifiers = [ + 'type_1', + ]; + + $this->nonDeletableTypeIdentifiersProvider->expects(static::once()) + ->method('getMethodTypeIdentifiers') + ->with($method) + ->willReturn($typeIdentifiers); + + $errorMessage = 'Error message'; + + $exception = new InvalidArgumentException($errorMessage); + + $this->methodTypeLabelsProvider->expects(static::once()) + ->method('getLabels') + ->with($methodId, $typeIdentifiers) + ->willThrowException($exception); + + $this->logger->expects(static::once()) + ->method('error') + ->with( + $errorMessage, + [ + 'method_identifier' => $methodId, + 'type_identifiers' => $typeIdentifiers, + ] + ); + + static::assertSame($result, $this->validator->validate($method)); + } + + public function testValidate() + { + $methodId = 'method_1'; + + $method = $this->getShippingMethodMock(); + $method->expects(static::once()) + ->method('getIdentifier') + ->willReturn($methodId); + + $result = $this->createMock(ShippingMethodValidatorResultInterface::class); + + $this->parentShippingMethodValidator->expects(static::once()) + ->method('validate') + ->with($method) + ->willReturn($result); + + $typeIdentifiers = [ + 'type_1', + 'type_2', + ]; + + $typeLabels = [ + 'Label 1', + 'Label 2', + ]; + + $this->nonDeletableTypeIdentifiersProvider->expects(static::once()) + ->method('getMethodTypeIdentifiers') + ->with($method) + ->willReturn($typeIdentifiers); + + $this->methodTypeLabelsProvider->expects(static::once()) + ->method('getLabels') + ->with($methodId, $typeIdentifiers) + ->willReturn($typeLabels); + + $translatedMessage = 'validation message'; + + $this->translator->expects(static::once()) + ->method('trans') + ->with( + 'marello.shipping.method_type.used.error', + ['%types%' => implode(', ', $typeLabels)] + ) + ->willReturn($translatedMessage); + + $clonedAndBuiltErrorCollection = $this->createErrorCollectionMock($result, $translatedMessage); + + $errorResult = $this->createErrorResultMock($result, $clonedAndBuiltErrorCollection); + + static::assertSame($errorResult, $this->validator->validate($method)); + } + + /** + * @param \PHPUnit\Framework\MockObject\MockObject $result + * @param string $translatedMessage + * + * @return Validator\Result\Error\Collection\ShippingMethodValidatorResultErrorCollectionInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createErrorCollectionMock(\PHPUnit\Framework\MockObject\MockObject $result, $translatedMessage) + { + $errorCollection = $this->createMock( + Validator\Result\Error\Collection\ShippingMethodValidatorResultErrorCollectionInterface::class + ); + + $builder = $this->createMock( + Validator\Result\Error\Collection\Builder\Common\ + CommonShippingMethodValidatorResultErrorCollectionBuilderInterface::class + ); + + $errorCollection->expects(static::once()) + ->method('createCommonBuilder') + ->willReturn($builder); + + $result->expects(static::any()) + ->method('getErrors') + ->willReturn($errorCollection); + + $error = $this->createMock(Validator\Result\Error\ShippingMethodValidatorResultErrorInterface::class); + + $this->errorFactory->expects(static::once()) + ->method('createError') + ->with($translatedMessage) + ->willReturn($error); + + $builder->expects(static::once()) + ->method('cloneAndBuild') + ->with($errorCollection) + ->willReturn($builder); + + $builder->expects(static::once()) + ->method('addError') + ->with($error) + ->willReturn($builder); + + $clonedAndBuiltErrorCollection = $this->createMock( + Validator\Result\Error\Collection\ShippingMethodValidatorResultErrorCollectionInterface::class + ); + + $builder->expects(static::once()) + ->method('getCollection') + ->willReturn($clonedAndBuiltErrorCollection); + + return $clonedAndBuiltErrorCollection; + } + + /** + * @param \PHPUnit\Framework\MockObject\MockObject $result + * @param Validator\Result\Error\Collection\ShippingMethodValidatorResultErrorCollectionInterface $errorCollection + * + * @return ShippingMethodValidatorResultInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createErrorResultMock( + \PHPUnit\Framework\MockObject\MockObject $result, + Validator\Result\Error\Collection\ShippingMethodValidatorResultErrorCollectionInterface $errorCollection + ) { + $resultFactory = $this->createMock( + Validator\Result\Factory\Common\CommonShippingMethodValidatorResultFactoryInterface::class + ); + + $result->expects(static::once()) + ->method('createCommonFactory') + ->willReturn($resultFactory); + + $errorResult = $this->createMock(ShippingMethodValidatorResultInterface::class); + + $resultFactory->expects(static::once()) + ->method('createErrorResult') + ->with($errorCollection) + ->willReturn($errorResult); + + return $errorResult; + } + + /** + * @param string $methodIdentifier + * @param string[] $methodTypeIdentifiers + * + * @return string[] + */ + private function getShippingMethodTypesLabels($methodIdentifier, array $methodTypeIdentifiers) + { + try { + return $this->methodTypeLabelsProvider->getLabels($methodIdentifier, $methodTypeIdentifiers); + } catch (InvalidArgumentException $exception) { + $this->logger->error($exception->getMessage(), [ + 'method_identifier' => $methodIdentifier, + 'type_identifiers' => $methodTypeIdentifiers, + ]); + + return []; + } + } + + /** + * @return ShippingMethodInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function getShippingMethodMock() + { + return $this->createMock(ShippingMethodInterface::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Builder/Common/Doctrine/DoctrineCommonShippingMethodValidatorResultErrorCollectionBuilderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Builder/Common/Doctrine/DoctrineCommonShippingMethodValidatorResultErrorCollectionBuilderTest.php new file mode 100644 index 000000000..43a5078e8 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Builder/Common/Doctrine/DoctrineCommonShippingMethodValidatorResultErrorCollectionBuilderTest.php @@ -0,0 +1,41 @@ +createMock(Error\ShippingMethodValidatorResultErrorInterface::class); + + $builder = new Builder\Common\Doctrine\DoctrineCommonShippingMethodValidatorResultErrorCollectionBuilder(); + + static::assertSame($builder, $builder->addError($error)); + + static::assertEquals( + new Error\Collection\Doctrine\DoctrineShippingMethodValidatorResultErrorCollection([$error]), + $builder->getCollection() + ); + } + + public function testCloneAndBuild() + { + /** @var Error\ShippingMethodValidatorResultErrorInterface $error */ + $error = $this->createMock(Error\ShippingMethodValidatorResultErrorInterface::class); + + $errorCollection = new Error\Collection\Doctrine\DoctrineShippingMethodValidatorResultErrorCollection([$error]); + + $builder = new Builder\Common\Doctrine\DoctrineCommonShippingMethodValidatorResultErrorCollectionBuilder(); + + static::assertSame($builder, $builder->cloneAndBuild($errorCollection)); + + static::assertEquals( + $errorCollection, + $builder->getCollection() + ); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Doctrine/DoctrineShippingMethodValidatorResultErrorCollectionTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Doctrine/DoctrineShippingMethodValidatorResultErrorCollectionTest.php new file mode 100644 index 000000000..0a8000caa --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Collection/Doctrine/DoctrineShippingMethodValidatorResultErrorCollectionTest.php @@ -0,0 +1,18 @@ +createCommonBuilder() + ); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultErrorFactoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultErrorFactoryTest.php new file mode 100644 index 000000000..b971c5efc --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultErrorFactoryTest.php @@ -0,0 +1,22 @@ + $message, + ] + ), $factory->createError($message)); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/ParameterBag/ParameterBagShippingMethodValidatorResultErrorTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/ParameterBag/ParameterBagShippingMethodValidatorResultErrorTest.php new file mode 100644 index 000000000..d5c6f6f05 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Error/ParameterBag/ParameterBagShippingMethodValidatorResultErrorTest.php @@ -0,0 +1,17 @@ + $message, + ]); + static::assertEquals($message, $error->getMessage()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultFactoryTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultFactoryTest.php new file mode 100644 index 000000000..84f2ebbb0 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/Factory/Common/ParameterBag/ParameterBagCommonShippingMethodValidatorResultFactoryTest.php @@ -0,0 +1,43 @@ +factory = new Factory\Common\ParameterBag\ParameterBagCommonShippingMethodValidatorResultFactory(); + } + + public function testCreateSuccessResult() + { + static::assertEquals(new ParameterBagShippingMethodValidatorResult( + [ + 'errors' => new Error\Collection\Doctrine\DoctrineShippingMethodValidatorResultErrorCollection(), + ] + ), $this->factory->createSuccessResult()); + } + + public function testCreateErrorResult() + { + /** @var Error\Collection\ShippingMethodValidatorResultErrorCollectionInterface $errors */ + $errors = $this->createMock(Error\Collection\ShippingMethodValidatorResultErrorCollectionInterface::class); + static::assertEquals(new ParameterBagShippingMethodValidatorResult( + [ + 'errors' => $errors, + ] + ), $this->factory->createErrorResult($errors)); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/ParameterBag/ParameterBagShippingMethodValidatorResultTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/ParameterBag/ParameterBagShippingMethodValidatorResultTest.php new file mode 100644 index 000000000..4e28dd3f2 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Method/Validator/Result/ParameterBag/ParameterBagShippingMethodValidatorResultTest.php @@ -0,0 +1,28 @@ +createCommonFactory() + ); + } + + public function testGetErrors() + { + $errors = new \ArrayObject(); + $result = new ParameterBagShippingMethodValidatorResult([ + 'errors' => $errors, + ]); + static::assertSame($errors, $result->getErrors()); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/BasicShippingMethodChoicesProviderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/BasicShippingMethodChoicesProviderTest.php new file mode 100644 index 000000000..f2b8e4249 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/BasicShippingMethodChoicesProviderTest.php @@ -0,0 +1,135 @@ +shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + $this->translator = $this->createMock(TranslatorInterface::class); + $this->choicesProvider = new BasicShippingMethodChoicesProvider( + $this->shippingMethodProvider, + $this->translator + ); + } + + /** + * @param array $methods + * @param array $result + * @param bool $translate + * + * @dataProvider methodsProvider + */ + public function testGetMethods($methods, $result, $translate = false) + { + $translation = [ + ['flat rate', [], null, null, 'flat rate translated'], + ['ups', [], null, null, 'ups translated'], + ]; + + $this->shippingMethodProvider->expects($this->once()) + ->method('getShippingMethods') + ->willReturn($methods); + + $this->translator->expects($this->any()) + ->method('trans') + ->will($this->returnValueMap($translation)); + + $this->assertEquals($result, $this->choicesProvider->getMethods($translate)); + } + + /** + * @return array + */ + public function methodsProvider() + { + return + [ + 'some_methods' => + [ + 'methods' => + [ + 'ups' => $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'ups', + 'sortOrder' => 1, + 'label' => 'ups', + 'isEnabled' => false, + 'types' => [], + ] + ), + 'flat_rate' => $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'label' => 'flat rate', + 'isEnabled' => true, + 'types' => [], + ] + ), + ], + 'result' => ['ups' => 'ups', 'flat_rate' => 'flat rate'], + 'translate' => false, + ], + 'some_methods_with_translation' => + [ + 'methods' => + [ + 'flat_rate' => $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'label' => 'flat rate', + 'isEnabled' => true, + 'types' => [], + ] + ), + 'ups' => $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'ups', + 'sortOrder' => 1, + 'label' => 'ups', + 'isEnabled' => false, + 'types' => [], + ] + ), + ], + 'result' => ['flat_rate' => 'flat rate translated', 'ups' => 'ups translated'], + 'translate' => true, + ], + 'no_methods' => + [ + 'methods' => [], + 'result' => [], + ], + ]; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Cache/ShippingPriceCacheTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Cache/ShippingPriceCacheTest.php new file mode 100644 index 000000000..548b822cd --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Cache/ShippingPriceCacheTest.php @@ -0,0 +1,152 @@ +cacheProvider = $this->getMockBuilder(CacheProvider::class) + ->setMethods(['fetch', 'contains', 'save', 'deleteAll'])->getMockForAbstractClass(); + + $this->keyGenerator = $this->createMock(ShippingContextCacheKeyGenerator::class); + $this->keyGenerator->expects(static::any()) + ->method('generateKey') + ->will(static::returnCallback(function (ShippingContextInterface $context) { + return ($context->getSourceEntity() ? get_class($context->getSourceEntity()) : '') + .'_'.$context->getSourceEntityIdentifier(); + })); + + $this->cache = new ShippingPriceCache($this->cacheProvider, $this->keyGenerator); + } + + /** + * @dataProvider hasPriceDataProvider + * @param boolean $isContains + * @param boolean $hasPrice + */ + public function testHasPrice($isContains, $hasPrice) + { + $context = $this->createShippingContext([]); + + $this->cacheProvider->expects(static::once()) + ->method('contains') + ->with('_flat_rateprimary') + ->willReturn($isContains); + + static::assertEquals($hasPrice, $this->cache->hasPrice($context, 'flat_rate', 'primary')); + } + + + public function hasPriceDataProvider() + { + return [ + [ + 'isContains' => true, + 'hasPrice' => true, + ], + [ + 'isContains' => false, + 'hasPrice' => false, + ] + ]; + } + + /** + * @dataProvider getPriceDataProvider + * @param boolean $isContains + * @param Price|null $price + */ + public function testGetPrice($isContains, Price $price = null) + { + $context = $this->createShippingContext([]); + $this->cacheProvider->expects(static::any()) + ->method('contains') + ->with('_flat_rateprimary') + ->willReturn($isContains); + $this->cacheProvider->expects(static::any()) + ->method('fetch') + ->with('_flat_rateprimary') + ->willReturn($isContains ? $price : false); + + static::assertSame($price, $this->cache->getPrice($context, 'flat_rate', 'primary')); + } + + public function getPriceDataProvider() + { + return [ + [ + 'isContains' => true, + 'price' => Price::create(5, 'USD'), + ], + [ + 'isContains' => false, + 'price' => null, + ] + ]; + } + + public function testSavePrice() + { + $context = $this->createShippingContext([ + ShippingContext::FIELD_SOURCE_ENTITY => new \stdClass(), + ShippingContext::FIELD_SOURCE_ENTITY_ID => 1 + ]); + + $price = Price::create(10, 'USD'); + + $this->cacheProvider->expects(static::once()) + ->method('save') + ->with('stdClass_1flat_rateprimary', $price, ShippingPriceCache::CACHE_LIFETIME) + ->willReturn($price); + + static::assertEquals($this->cache, $this->cache->savePrice($context, 'flat_rate', 'primary', $price)); + } + + public function testDeleteAllPrices() + { + $this->cacheProvider->expects(static::once()) + ->method('deleteAll'); + + $this->cache->deleteAllPrices(); + } + + /** + * @param array $params + * + * @return ShippingContext + */ + private function createShippingContext(array $params) + { + $actualParams = array_merge([ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection([]) + ], $params); + + return new ShippingContext($actualParams); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledMethodsShippingPriceProviderDecoratorTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledMethodsShippingPriceProviderDecoratorTest.php new file mode 100644 index 000000000..d811d6816 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledMethodsShippingPriceProviderDecoratorTest.php @@ -0,0 +1,158 @@ +shippingPriceProvider = $this->createMock(ShippingPriceProviderInterface::class); + $this->shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + $this->decorator = new EnabledMethodsShippingPriceProviderDecorator( + $this->shippingPriceProvider, + $this->shippingMethodProvider + ); + } + + /** + * @param array $methods + * @param array $methodViews + * @param array $expectedMethodViews + * @dataProvider getApplicableMethodsViewsProvider + */ + public function testGetApplicableMethodsViews($methods, $methodViews, $expectedMethodViews) + { + $context = $this->createMock(ShippingContext::class); + + $methodViewCollection = new ShippingMethodViewCollection(); + foreach ($methodViews as $id => $view) { + $methodViewCollection ->addMethodView($id, $view); + } + + $expectedCollection = new ShippingMethodViewCollection(); + foreach ($expectedMethodViews as $id => $view) { + $expectedCollection ->addMethodView($id, $view); + } + + $this->shippingPriceProvider->expects($this->any()) + ->method('getApplicableMethodsViews') + ->with($context) + ->willReturn($methodViewCollection); + + $this->shippingMethodProvider->expects($this->any()) + ->method('getShippingMethod') + ->will($this->returnCallback(function ($methodId) use ($methods) { + return array_key_exists($methodId, $methods) ? $methods[$methodId] : null; + })); + + $this->assertEquals($expectedCollection, $this->decorator->getApplicableMethodsViews($context)); + } + + /** + * @return array + */ + public function getApplicableMethodsViewsProvider() + { + return [ + 'all_methods_enabled' => [ + 'methods' => [ + 'flat_rate' => $this->getEntity(ShippingMethodStub::class, [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'isEnabled' => true, + 'types' => [] + ]), + 'ups' => $this->getEntity(PriceAwareShippingMethodStub::class, [ + 'identifier' => 'ups', + 'sortOrder' => 2, + 'isEnabled' => true, + 'types' => [] + ]) + ], + 'method_views' => [ + 'flat_rate' => ['flat_rate', false, 'flat_rate', 1], + 'ups' => ['ups', false, 'ups', 2], + ], + 'expected_method_views' => [ + 'flat_rate' => ['flat_rate', false, 'flat_rate', 1], + 'ups' => ['ups', false, 'ups', 2], + ] + ], + 'all_methods_disabled' => [ + 'methods' => [ + 'flat_rate' => $this->getEntity(ShippingMethodStub::class, [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'isEnabled' => false, + 'types' => [] + ]), + 'ups' => $this->getEntity(PriceAwareShippingMethodStub::class, [ + 'identifier' => 'ups', + 'sortOrder' => 2, + 'isEnabled' => false, + 'types' => [] + ]) + ], + 'method_views' => [ + 'flat_rate' => ['flat_rate', false, 'flat_rate', 1], + 'ups' => ['ups', false, 'ups', 2], + ], + 'expected_method_views' => [] + ], + 'some_methods_enabled' => [ + 'methods' => [ + 'flat_rate' => $this->getEntity(ShippingMethodStub::class, [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'isEnabled' => true, + 'types' => [] + ]), + 'ups' => $this->getEntity(PriceAwareShippingMethodStub::class, [ + 'identifier' => 'ups', + 'sortOrder' => 2, + 'isEnabled' => false, + 'types' => [] + ]) + ], + 'method_views' => [ + 'flat_rate' => ['flat_rate', false, 'flat_rate', 1], + 'ups' => ['ups', false, 'ups', 2], + ], + 'expected_method_views' => [ + 'flat_rate' => ['flat_rate', false, 'flat_rate', 1], + ] + ], + 'no_methods' => [ + 'methods' => [], + 'method_views' => [], + 'expected_method_views' => [] + ] + ]; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledShippingMethodChoicesProviderDecoratorTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledShippingMethodChoicesProviderDecoratorTest.php new file mode 100644 index 000000000..58ad6aa23 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/EnabledShippingMethodChoicesProviderDecoratorTest.php @@ -0,0 +1,178 @@ +shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + $this->choicesProvider = $this->createMock(ShippingMethodChoicesProviderInterface::class); + $this->enabledChoicesProvider = new EnabledShippingMethodChoicesProviderDecorator( + $this->shippingMethodProvider, + $this->choicesProvider + ); + } + + /** + * @param array $registryMap + * @param array $choices + * @param array $result + * + * @dataProvider methodsProvider + */ + public function testGetMethods($registryMap, $choices, $result) + { + $this->shippingMethodProvider->expects($this->any()) + ->method('getShippingMethod') + ->will($this->returnValueMap($registryMap)); + + $this->choicesProvider->expects($this->once()) + ->method('getMethods') + ->willReturn($choices); + + $this->assertEquals($result, $this->enabledChoicesProvider->getMethods()); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function methodsProvider() + { + return + [ + 'all_methods_enabled' => + [ + 'methods_map' => + [ + [ + 'flat_rate', + $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'label' => 'flat rate', + 'isEnabled' => true, + 'types' => [], + ] + ), + ], + [ + 'ups', + $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'ups', + 'sortOrder' => 1, + 'label' => 'ups', + 'isEnabled' => true, + 'types' => [], + ] + ), + ], + ], + 'choices' => ['flat_rate' => 'flat rate', 'ups' => 'ups'], + 'result' => ['ups' => 'ups', 'flat_rate' => 'flat rate'], + ], + 'some_methods_disabled' => + [ + 'methods_map' => + [ + [ + 'flat_rate', + $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'label' => 'flat rate', + 'isEnabled' => true, + 'types' => [], + ] + ), + ], + [ + 'ups', + $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'ups', + 'sortOrder' => 1, + 'label' => 'ups', + 'isEnabled' => false, + 'types' => [], + ] + ), + ], + ], + 'choices' => ['flat_rate' => 'flat rate', 'ups' => 'ups'], + 'result' => ['flat_rate' => 'flat rate',], + ], + 'all_disabled_methods' => + [ + 'methods_map' => + [ + [ + 'flat_rate', + $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'label' => 'flat rate', + 'isEnabled' => false, + 'types' => [], + ] + ), + ], + [ + 'ups', + $this->getEntity( + ShippingMethodStub::class, + [ + 'identifier' => 'ups', + 'sortOrder' => 1, + 'label' => 'ups', + 'isEnabled' => false, + 'types' => [], + ] + ), + ], + ], + 'choices' => ['flat rate' => 'flat_rate', 'ups' => 'ups'], + 'result' => [], + ], + 'no_methods' => + [ + 'methods' => [], + 'choices' => [], + 'result' => [], + ], + ]; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php new file mode 100644 index 000000000..da4a768e3 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/Basic/BasicMethodsConfigsRulesByContextProviderTest.php @@ -0,0 +1,114 @@ +repository = $this->createMock(ShippingMethodsConfigsRuleRepository::class); + + $this->filtrationService = $this->createMock(MethodsConfigsRulesFiltrationServiceInterface::class); + + $this->provider = new BasicMethodsConfigsRulesByContextProvider( + $this->filtrationService, + $this->repository + ); + } + + public function testGetAllFilteredShippingMethodsConfigsWithShippingAddress() + { + $currency = 'USD'; + $address = $this->createAddressMock(); + $rulesFromDb = [ + $this->createShippingMethodsConfigsRuleMock(), + $this->createShippingMethodsConfigsRuleMock(), + ]; + + $this->repository->expects(static::once()) + ->method('getByDestinationAndCurrency') + ->with($address, $currency) + ->willReturn($rulesFromDb); + + $this->repository->expects(static::never()) + ->method('getByCurrencyWithoutDestination'); + + $context = $this->createShippingContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + $context->method('getShippingAddress') + ->willReturn($address); + + $expectedRules = [$this->createShippingMethodsConfigsRuleMock()]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredShippingMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getShippingMethodsConfigsRules($context) + ); + } + + public function testGetAllFilteredShippingMethodsConfigsWithoutShippingAddress() + { + $currency = 'USD'; + $rulesFromDb = [$this->createShippingMethodsConfigsRuleMock()]; + + $this->repository->expects(static::once()) + ->method('getByCurrencyWithoutDestination') + ->with($currency) + ->willReturn($rulesFromDb); + + $context = $this->createShippingContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + + $expectedRules = [$this->createShippingMethodsConfigsRuleMock()]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredShippingMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getShippingMethodsConfigsRules($context) + ); + } + + /** + * @return AddressInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createAddressMock() + { + return $this->createMock(AddressInterface::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php new file mode 100644 index 000000000..0b6dbe2db --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/MethodsConfigsRule/Context/RegardlessDestination/RegardlessDestinationMethodsConfigsRulesByContextProviderTest.php @@ -0,0 +1,115 @@ +repository = $this->createMock(ShippingMethodsConfigsRuleRepository::class); + + $this->filtrationService = $this->createMock(MethodsConfigsRulesFiltrationServiceInterface::class); + + $this->provider = new RegardlessDestination\RegardlessDestinationMethodsConfigsRulesByContextProvider( + $this->filtrationService, + $this->repository + ); + } + + public function testGetAllFilteredShippingMethodsConfigsWithShippingAddress() + { + $currency = 'USD'; + $address = $this->createAddressMock(); + $rulesFromDb = [$this->createShippingMethodsConfigsRuleMock()]; + + $this->repository->expects(static::once()) + ->method('getByDestinationAndCurrency') + ->with($address, $currency) + ->willReturn($rulesFromDb); + + $this->repository->expects(static::never()) + ->method('getByCurrency'); + + $context = $this->createShippingContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + $context->method('getShippingAddress') + ->willReturn($address); + + $expectedRules = [ + $this->createShippingMethodsConfigsRuleMock(), + $this->createShippingMethodsConfigsRuleMock(), + ]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredShippingMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getShippingMethodsConfigsRules($context) + ); + } + + public function testGetAllFilteredShippingMethodsConfigsWithoutShippingAddress() + { + $currency = 'USD'; + $rulesFromDb = [$this->createShippingMethodsConfigsRuleMock()]; + + $this->repository->expects(static::once()) + ->method('getByCurrency') + ->with($currency) + ->willReturn($rulesFromDb); + + $context = $this->createShippingContextMock(); + $context->method('getCurrency') + ->willReturn($currency); + + $expectedRules = [$this->createShippingMethodsConfigsRuleMock()]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredShippingMethodsConfigsRules') + ->with($rulesFromDb) + ->willReturn($expectedRules); + + static::assertSame( + $expectedRules, + $this->provider->getShippingMethodsConfigsRules($context) + ); + } + + /** + * @return AddressInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createAddressMock() + { + return $this->createMock(AddressInterface::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/PriceAwareShippingMethodStub.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/PriceAwareShippingMethodStub.php new file mode 100644 index 000000000..e0bc41d18 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/PriceAwareShippingMethodStub.php @@ -0,0 +1,19 @@ +setIdentifier(self::METHOD_TYPE_IDENTIFIER); + + $method = new ShippingMethodStub(); + $method->setIdentifier(self::METHOD_IDENTIFIER) + ->setIsGrouped(false) + ->setTypes([$type]); + + $this->method = $method; + } + + /** + * {@inheritdoc} + */ + public function getShippingMethods() + { + return [$this->method->getIdentifier() => $this->method]; + } + + /** + * {@inheritdoc} + */ + public function getShippingMethod($name) + { + if ($name === $this->method->getIdentifier()) { + return $this->method; + } + return null; + } + + /** + * {@inheritdoc} + */ + public function hasShippingMethod($name) + { + return $name === $this->method->getIdentifier(); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodStub.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodStub.php new file mode 100644 index 000000000..52149a94c --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodStub.php @@ -0,0 +1,181 @@ +types; + } + + /** + * @param ShippingMethodTypeStub[] $types + * @return $this + */ + public function setTypes($types) + { + $this->types = $types; + + return $this; + } + + /** + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * @param string $identifier + * @return null|ShippingMethodTypeStub + */ + public function getType($identifier) + { + foreach ($this->types as $type) { + if ($type->getIdentifier() === $identifier) { + return $type; + } + } + + return null; + } + + /** + * @param string $identifier + * @return $this + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + + return $this; + } + + /** + * @return string + */ + public function getLabel() + { + return $this->label ?: $this->identifier . '.label'; + } + + /** + * @param string $label + * @return $this + */ + public function setLabel($label) + { + $this->label = $label; + + return $this; + } + + /** + * @return int + */ + public function getSortOrder() + { + return $this->sortOrder; + } + + /** + * @param int $sortOrder + * @return $this + */ + public function setSortOrder($sortOrder) + { + $this->sortOrder = $sortOrder; + + return $this; + } + + /** + * @return string + */ + public function getOptionsConfigurationFormType() + { + return $this->optionsConfigurationFormType; + } + + /** + * @param string $optionsConfigurationFormType + * @return $this + */ + public function setOptionsConfigurationFormType($optionsConfigurationFormType) + { + $this->optionsConfigurationFormType = $optionsConfigurationFormType; + + return $this; + } + + /** + * @return boolean + */ + public function isGrouped() + { + return $this->isGrouped; + } + + /** + * @return boolean + */ + public function isEnabled() + { + return $this->isEnabled; + } + + /** + * @param boolean $isGrouped + * @return $this + */ + public function setIsGrouped($isGrouped) + { + $this->isGrouped = $isGrouped; + + return $this; + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeConfigTypeOptionsStub.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeConfigTypeOptionsStub.php new file mode 100644 index 000000000..52235875a --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeConfigTypeOptionsStub.php @@ -0,0 +1,22 @@ +add('price', TextType::class) + ->add('handling_fee', TextType::class) + ->add('type', TextType::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeStub.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeStub.php new file mode 100644 index 000000000..99432c419 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/Stub/ShippingMethodTypeStub.php @@ -0,0 +1,120 @@ +identifier; + } + + /** + * @param string $identifier + * @return $this + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + return $this; + } + + /** + * @return string + */ + public function getLabel() + { + return $this->label ?: $this->identifier . '.label'; + } + + /** + * @param string $label + * @return $this + */ + public function setLabel($label) + { + $this->label = $label; + return $this; + } + + /** + * @return int + */ + public function getSortOrder() + { + return $this->sortOrder; + } + + /** + * @param int $sortOrder + * @return $this + */ + public function setSortOrder($sortOrder) + { + $this->sortOrder = $sortOrder; + return $this; + } + + /** + * @return string + */ + public function getOptionsConfigurationFormType() + { + return $this->optionsConfigurationFormType; + } + + /** + * @param string $optionsConfigurationFormType + * @return $this + */ + public function setOptionsConfigurationFormType($optionsConfigurationFormType) + { + $this->optionsConfigurationFormType = $optionsConfigurationFormType; + return $this; + } + + /** + * {@inheritdoc} + */ + public function calculatePrice(ShippingContextInterface $context, array $methodOptions, array $typeOptions) + { + return $typeOptions['price']; + } + + /** + * @inheritDoc + */ + public function createShipment(ShippingContextInterface $context, $method, $type) + { + // TODO: Implement createShipment() method. + } + + +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/test/ShippingPriceProviderTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/test/ShippingPriceProviderTest.php new file mode 100644 index 000000000..aacba5fb1 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Provider/test/ShippingPriceProviderTest.php @@ -0,0 +1,630 @@ +shippingRulesProvider = $this->getMockBuilder(MethodsConfigsRulesByContextProviderInterface::class) + ->disableOriginalConstructor()->getMock(); + + $methods = [ + 'flat_rate' => $this->getEntity(ShippingMethodStub::class, [ + 'identifier' => 'flat_rate', + 'sortOrder' => 1, + 'types' => [ + 'primary' => $this->getEntity(ShippingMethodTypeStub::class, [ + 'identifier' => 'primary', + 'sortOrder' => 1, + ]) + ] + ]), + 'integration_method' => $this->getEntity(PriceAwareShippingMethodStub::class, [ + 'identifier' => 'integration_method', + 'sortOrder' => 2, + 'isGrouped' => true, + 'types' => [ + 'ground' => $this->getEntity(ShippingMethodTypeStub::class, [ + 'identifier' => 'ground', + 'sortOrder' => 1, + ]), + 'air' => $this->getEntity(ShippingMethodTypeStub::class, [ + 'identifier' => 'air', + 'sortOrder' => 2, + ]) + ] + ]) + ]; + + $this->shippingMethodProvider = $this->createMock(ShippingMethodProviderInterface::class); + $this->shippingMethodProvider->expects($this->any()) + ->method('getShippingMethod') + ->will($this->returnCallback(function ($methodId) use ($methods) { + return array_key_exists($methodId, $methods) ? $methods[$methodId] : null; + })); + + $this->priceCache = $this->getMockBuilder(ShippingPriceCache::class) + ->disableOriginalConstructor()->getMock(); + + $viewFactory = new ShippingMethodViewFactory($this->shippingMethodProvider); + + $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $this->shippingPriceProvider = new ShippingPriceProvider( + $this->shippingRulesProvider, + $this->shippingMethodProvider, + $this->priceCache, + $viewFactory, + $this->eventDispatcher + ); + } + + /** + * @dataProvider getApplicableShippingMethodsConfigsRulesProvider + * + * @param array $shippingRules + * @param array $expectedData + */ + public function testGetApplicableMethodsViews(array $shippingRules, array $expectedData) + { + $shippingLineItems = [new ShippingLineItem([])]; + + $sourceEntity = new \stdClass(); + + $context = new ShippingContext([ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection($shippingLineItems), + ShippingContext::FIELD_CURRENCY => 'USD', + ShippingContext::FIELD_SOURCE_ENTITY => $sourceEntity + ]); + + $this->shippingRulesProvider->expects($this->once()) + ->method('getShippingMethodsConfigsRules') + ->with($context) + ->willReturn($shippingRules); + + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(ApplicableMethodsEvent::NAME); + + $this->assertEquals( + $expectedData, + $this->shippingPriceProvider + ->getApplicableMethodsViews($context) + ->toArray() + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function getApplicableShippingMethodsConfigsRulesProvider() + { + return [ + 'one rule' => [ + 'shippingRule' => [ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'flat_rate', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'primary', + 'options' => [ + 'price' => Price::create(12, 'USD'), + ], + ]) + ], + ]) + ] + ]), + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'integration_method', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'ground', + 'options' => [ + 'aware_price' => null, + ], + ]) + ], + ]) + ] + ]), + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'unknown_method', + ]) + ] + ]) + ], + 'expectedData' => [ + 'flat_rate' => [ + 'identifier' => 'flat_rate', + 'label' => 'flat_rate.label', + 'sortOrder' => 1, + 'isGrouped' => false, + 'types' => [ + 'primary' => [ + 'identifier' => 'primary', + 'label' => 'primary.label', + 'sortOrder' => 1, + 'price' => Price::create(12, 'USD') + ] + ] + ] + ] + ], + 'several rules with same methods ans diff types' => [ + 'shippingRule' => [ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'integration_method', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'ground', + 'options' => [ + 'aware_price' => Price::create(2, 'USD'), + ], + ]), + ], + ]), + ] + ]), + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'flat_rate', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'primary', + 'options' => [ + 'price' => Price::create(1, 'USD'), + ], + ]) + ], + ]) + ] + ]), + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'integration_method', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'ground', + 'options' => [ + 'aware_price' => Price::create(3, 'USD'), + ], + ]), + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'air', + 'options' => [ + 'aware_price' => Price::create(4, 'USD'), + ], + ]), + ], + ]), + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'flat_rate', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'primary', + 'options' => [ + 'price' => Price::create(5, 'USD'), + ], + ]) + ], + ]) + ] + ]), + ], + 'expectedData' => [ + 'flat_rate' => [ + 'identifier' => 'flat_rate', + 'label' => 'flat_rate.label', + 'sortOrder' => 1, + 'isGrouped' => false, + 'types' => [ + 'primary' => [ + 'identifier' => 'primary', + 'label' => 'primary.label', + 'sortOrder' => 1, + 'price' => Price::create(1, 'USD'), + ] + ] + ], + 'integration_method' => [ + 'identifier' => 'integration_method', + 'label' => 'integration_method.label', + 'sortOrder' => 2, + 'isGrouped' => true, + 'types' => [ + 'ground' => [ + 'identifier' => 'ground', + 'label' => 'ground.label', + 'sortOrder' => 1, + 'price' => Price::create(2, 'USD'), + ], + 'air' => [ + 'identifier' => 'air', + 'label' => 'air.label', + 'sortOrder' => 2, + 'price' => Price::create(4, 'USD'), + ] + ] + ] + ] + ], + ]; + } + + public function testGetApplicableMethodsViewsCache() + { + $shippingLineItems = [new ShippingLineItem([])]; + + $context = new ShippingContext([ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection($shippingLineItems), + ShippingContext::FIELD_CURRENCY => 'USD' + ]); + + $this->shippingRulesProvider->expects(static::exactly(2)) + ->method('getShippingMethodsConfigsRules') + ->with($context) + ->willReturn([ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'flat_rate', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'primary', + 'options' => [ + 'price' => Price::create(1, 'USD'), + ], + ]) + ], + ]) + ] + ]) + ]); + $price = Price::create(1, 'USD'); + + $expectedData = [ + 'flat_rate' => [ + 'identifier' => 'flat_rate', + 'label' => 'flat_rate.label', + 'sortOrder' => 1, + 'isGrouped' => false, + 'types' => [ + 'primary' => [ + 'identifier' => 'primary', + 'label' => 'primary.label', + 'sortOrder' => 1, + 'price' => $price + ] + ] + ] + ]; + + $this->priceCache->expects(static::at(0)) + ->method('hasPrice') + ->with($context, 'flat_rate', 'primary') + ->willReturn(false); + + $this->priceCache->expects(static::at(1)) + ->method('savePrice') + ->with($context, 'flat_rate', 'primary', Price::create(1, 'USD')) + ->willReturn(true); + + $this->priceCache->expects(static::at(2)) + ->method('hasPrice') + ->with($context, 'flat_rate', 'primary') + ->willReturn(true); + + $this->priceCache->expects(static::at(3)) + ->method('getPrice') + ->with($context, 'flat_rate', 'primary') + ->willReturn(Price::create(2, 'USD')); + + $this->assertEquals( + $expectedData, + $this->shippingPriceProvider->getApplicableMethodsViews($context)->toArray() + ); + $price->setValue(2); + $this->assertEquals( + $expectedData, + $this->shippingPriceProvider->getApplicableMethodsViews($context)->toArray() + ); + } + + /** + * @dataProvider getPriceDataProvider + * + * @param string $methodId + * @param string $typeId + * @param array $shippingRules + * @param Price|null $expectedPrice + */ + public function testGetPrice($methodId, $typeId, array $shippingRules, Price $expectedPrice = null) + { + $shippingLineItems = [new ShippingLineItem([])]; + + $context = new ShippingContext([ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection($shippingLineItems), + ShippingContext::FIELD_CURRENCY => 'USD' + ]); + + $this->shippingRulesProvider->expects($this->once()) + ->method('getShippingMethodsConfigsRules') + ->with($context) + ->willReturn($shippingRules); + + $this->priceCache->expects($this->exactly($expectedPrice ? 1 : 0))->method('savePrice'); + + $this->assertEquals($expectedPrice, $this->shippingPriceProvider->getPrice($context, $methodId, $typeId)); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function getPriceDataProvider() + { + return [ + 'no rule' => [ + 'methodId' => 'integration_method', + 'typeId' => 'ground', + 'shippingRules' => [ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'flat_rate', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'primary', + 'options' => [ + 'price' => Price::create(12, 'USD'), + ], + ]) + ], + ]) + ] + ]), + ], + 'expectedData' => null, + ], + 'one rule' => [ + 'methodId' => 'flat_rate', + 'typeId' => 'primary', + 'shippingRules' => [ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'flat_rate', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'primary', + 'options' => [ + 'price' => Price::create(12, 'USD'), + ], + ]) + ], + ]) + ] + ]), + ], + 'expectedData' => Price::create(12, 'USD'), + ], + 'no price' => [ + 'methodId' => 'flat_rate', + 'typeId' => 'primary', + 'shippingRules' => [ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'flat_rate', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'primary', + 'options' => [ + 'price' => null + ], + ]) + ], + ]) + ] + ]), + ], + 'expectedData' => null, + ], + 'several rules with same methods ans types' => [ + 'methodId' => 'integration_method', + 'typeId' => 'ground', + 'shippingRules' => [ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'integration_method', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'ground', + 'options' => [ + 'price' => Price::create(1, 'USD'), + ], + ]) + ], + ]), + ] + ]), + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'integration_method', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'ground', + 'options' => [ + 'price' => Price::create(2, 'USD'), + ], + ]) + ], + ]), + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => 'integration_method', + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => 'air', + 'options' => [ + 'price' => Price::create(3, 'USD'), + ], + ]) + ], + ]) + ] + ]) + ], + 'expectedData' => Price::create(1, 'USD'), + ], + ]; + } + + public function testGetPriceCache() + { + $methodId = 'flat_rate'; + $typeId = 'primary'; + + $shippingLineItems = [new ShippingLineItem([])]; + + $context = new ShippingContext([ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection($shippingLineItems), + ShippingContext::FIELD_CURRENCY => 'USD' + ]); + + $this->shippingRulesProvider->expects($this->exactly(2)) + ->method('getShippingMethodsConfigsRules') + ->with($context) + ->willReturn([ + $this->getEntity(ShippingMethodsConfigsRule::class, [ + 'methodConfigs' => [ + $this->getEntity(ShippingMethodConfig::class, [ + 'method' => $methodId, + 'typeConfigs' => [ + $this->getEntity(ShippingMethodTypeConfig::class, [ + 'enabled' => true, + 'type' => $typeId, + 'options' => [ + 'price' => Price::create(1, 'USD'), + ], + ]) + ], + ]) + ] + ]) + ]); + + $expectedPrice = Price::create(1, 'USD'); + + $this->priceCache->expects(static::at(0)) + ->method('hasPrice') + ->with($context, 'flat_rate', 'primary') + ->willReturn(false); + + $this->priceCache->expects(static::at(1)) + ->method('savePrice') + ->with($context, 'flat_rate', 'primary', Price::create(1, 'USD')) + ->willReturn(true); + + $this->priceCache->expects(static::at(2)) + ->method('hasPrice') + ->with($context, 'flat_rate', 'primary') + ->willReturn(true); + + $this->priceCache->expects(static::at(3)) + ->method('getPrice') + ->with($context, 'flat_rate', 'primary') + ->willReturn(Price::create(2, 'USD')); + + $this->assertEquals($expectedPrice, $this->shippingPriceProvider->getPrice($context, $methodId, $typeId)); + $expectedPrice->setValue(2); + $this->assertEquals($expectedPrice, $this->shippingPriceProvider->getPrice($context, $methodId, $typeId)); + } + + public function testGetPriceNoMethodAndType() + { + $shippingLineItems = [new ShippingLineItem([])]; + + $context = new ShippingContext([ + ShippingContext::FIELD_LINE_ITEMS => new DoctrineShippingLineItemCollection($shippingLineItems), + ShippingContext::FIELD_CURRENCY => 'USD' + ]); + + $this->assertNull($this->shippingPriceProvider->getPrice($context, 'unknown_method', 'primary')); + $this->assertNull($this->shippingPriceProvider->getPrice($context, 'flat_rate', 'unknown_method')); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php new file mode 100644 index 000000000..bf5a47985 --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/RuleFiltration/Basic/BasicMethodsConfigsRulesFiltrationServiceTest.php @@ -0,0 +1,96 @@ +filtrationService = $this->createMock(RuleFiltrationServiceInterface::class); + $this->shippingContextToRuleValuesConverter = $this + ->createMock(ShippingContextToRulesValuesConverterInterface::class); + + $this->basicMethodsConfigsRulesFiltrationService = new BasicMethodsConfigsRulesFiltrationService( + $this->filtrationService, + $this->shippingContextToRuleValuesConverter + ); + } + + /** + * {@inheritDoc} + */ + public function testGetFilteredShippingMethodsConfigsRules() + { + $configRules = [ + $this->createShippingMethodsConfigsRule(), + $this->createShippingMethodsConfigsRule(), + ]; + $context = $this->createContextMock(); + $values = [ + 'currency' => 'USD', + ]; + + $this->shippingContextToRuleValuesConverter->expects(static::once()) + ->method('convert') + ->with($context) + ->willReturn($values); + + $expectedConfigRules = [ + $this->createShippingMethodsConfigsRule(), + $this->createShippingMethodsConfigsRule(), + ]; + + $this->filtrationService->expects(static::once()) + ->method('getFilteredRuleOwners') + ->with($configRules, $values) + ->willReturn($expectedConfigRules); + + static::assertEquals( + $expectedConfigRules, + $this->basicMethodsConfigsRulesFiltrationService->getFilteredShippingMethodsConfigsRules( + $configRules, + $context + ) + ); + } + + /** + * @return ShippingContextInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function createContextMock() + { + return $this->createMock(ShippingContextInterface::class); + } + + /** + * @return ShippingMethodsConfigsRule|\PHPUnit\Framework\MockObject\MockObject + */ + private function createShippingMethodsConfigsRule() + { + return $this->createMock(ShippingMethodsConfigsRule::class); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Tests/Unit/Tools/FilteredDatagridRouteHelperTest.php b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Tools/FilteredDatagridRouteHelperTest.php new file mode 100644 index 000000000..4b5f78ced --- /dev/null +++ b/src/Marello/Bundle/ShippingBundle/Tests/Unit/Tools/FilteredDatagridRouteHelperTest.php @@ -0,0 +1,67 @@ +datagridRouteHelper = $this->createMock(DatagridRouteHelper::class); + + $this->gridRouteName = 'route_name'; + $this->gridName = 'grid_name'; + + $this->helper = new FilteredDatagridRouteHelper( + $this->gridRouteName, + $this->gridName, + $this->datagridRouteHelper + ); + } + + /** + * {@inheritDoc} + */ + protected function tearDown() + { + unset($this->datagridRouteHelper, $this->helper); + } + + public function testGenerate() + { + $this->datagridRouteHelper->expects($this->once())->method('generate')->with( + $this->gridRouteName, + $this->gridName, + ['f' => ['filterName' => ['value' => ['' => '10']]]], + RouterInterface::ABSOLUTE_PATH + )->willReturn('generatedURL'); + + $this->assertEquals('generatedURL', $this->helper->generate(['filterName' => 10])); + } +} diff --git a/src/Marello/Bundle/ShippingBundle/Translator/ShippingMethodLabelTranslator.php b/src/Marello/Bundle/ShippingBundle/Translator/ShippingMethodLabelTranslator.php index c1f219755..eefd453f0 100755 --- a/src/Marello/Bundle/ShippingBundle/Translator/ShippingMethodLabelTranslator.php +++ b/src/Marello/Bundle/ShippingBundle/Translator/ShippingMethodLabelTranslator.php @@ -3,7 +3,7 @@ namespace Marello\Bundle\ShippingBundle\Translator; use Marello\Bundle\ShippingBundle\Formatter\ShippingMethodLabelFormatter; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class ShippingMethodLabelTranslator { diff --git a/src/Marello/Bundle/ShippingBundle/Twig/ShippingMethodExtension.php b/src/Marello/Bundle/ShippingBundle/Twig/ShippingMethodExtension.php index 388c649bf..e37601cb3 100755 --- a/src/Marello/Bundle/ShippingBundle/Twig/ShippingMethodExtension.php +++ b/src/Marello/Bundle/ShippingBundle/Twig/ShippingMethodExtension.php @@ -6,8 +6,10 @@ use Marello\Bundle\ShippingBundle\Event\ShippingMethodConfigDataEvent; use Marello\Bundle\ShippingBundle\Formatter\ShippingMethodLabelFormatter; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class ShippingMethodExtension extends \Twig_Extension +class ShippingMethodExtension extends AbstractExtension { const SHIPPING_METHOD_EXTENSION_NAME = 'marello_shipping_method'; const DEFAULT_METHOD_CONFIG_TEMPLATE @@ -92,23 +94,23 @@ public function isShippingMethodEnabled($methodIdentifier) public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_get_shipping_method_label', [$this->shippingMethodLabelFormatter, 'formatShippingMethodLabel'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_get_shipping_method_type_label', [$this->shippingMethodLabelFormatter, 'formatShippingMethodTypeLabel'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_shipping_method_with_type_label', [$this->shippingMethodLabelFormatter, 'formatShippingMethodWithTypeLabel'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_shipping_method_config_template', [$this, 'getShippingMethodConfigRenderData'] ), - new \Twig_SimpleFunction( + new TwigFunction( 'marello_shipping_method_enabled', [$this, 'isShippingMethodEnabled'] ) diff --git a/src/Marello/Bundle/SupplierBundle/Controller/SupplierController.php b/src/Marello/Bundle/SupplierBundle/Controller/SupplierController.php index e6f8dc310..414a8fb41 100644 --- a/src/Marello/Bundle/SupplierBundle/Controller/SupplierController.php +++ b/src/Marello/Bundle/SupplierBundle/Controller/SupplierController.php @@ -3,22 +3,26 @@ namespace Marello\Bundle\SupplierBundle\Controller; use Marello\Bundle\AddressBundle\Form\Type\AddressType; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Marello\Bundle\SupplierBundle\Entity\Supplier; use Marello\Bundle\AddressBundle\Entity\MarelloAddress; +use Symfony\Component\Routing\Annotation\Route; -class SupplierController extends Controller +class SupplierController extends AbstractController { /** - * @Config\Route("/", name="marello_supplier_supplier_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_supplier_supplier_index" + * ) + * @Template * @AclAncestor("marello_supplier_view") */ public function indexAction() @@ -27,8 +31,12 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_supplier_supplier_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_supplier_supplier_view" + * ) + * @Template * @AclAncestor("marello_supplier_view") * * @param Supplier $supplier @@ -41,32 +49,36 @@ public function viewAction(Supplier $supplier) } /** - * @Config\Route("/create", name="marello_supplier_supplier_create") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/create", + * methods={"GET", "POST"}, + * name="marello_supplier_supplier_create" + * ) + * @Template * @AclAncestor("marello_supplier_create") * - * @param Request $request - * * @return array */ - public function createAction(Request $request) + public function createAction() { return $this->update(new Supplier()); } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_supplier_supplier_update") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/update/{id}", + * methods={"GET", "POST"}, + * requirements={"id"="\d+"}, + * name="marello_supplier_supplier_update" + * ) + * @Template * @AclAncestor("marello_supplier_update") * - * @param Request $request - * @param Supplier $supplier + * @param Supplier $supplier * * @return array */ - public function updateAction(Request $request, Supplier $supplier) + public function updateAction(Supplier $supplier) { return $this->update($supplier); } @@ -112,21 +124,20 @@ protected function update(Supplier $supplier = null) } /** - * @Config\Route( - * "/widget/address/{id}/{typeId}", + * @Route( + * path="/widget/address/{id}/{typeId}", + * methods={"GET", "POST"}, * requirements={"id"="\d+","typeId"="\d+"}, * name="marello_supplier_supplier_address" * ) - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Template("MarelloSupplierBundle:Supplier/widget:address.html.twig") * @AclAncestor("marello_supplier_update") * - * @param Request $request * @param MarelloAddress $address * * @return array */ - public function addressAction(Request $request, MarelloAddress $address) + public function addressAction(MarelloAddress $address) { return [ 'supplierAddress' => $address @@ -134,9 +145,13 @@ public function addressAction(Request $request, MarelloAddress $address) } /** - * @Config\Route("/update/address/{id}", requirements={"id"="\d+"}, name="marello_supplier_supplier_updateaddress") - * @Config\Method({"GET", "POST"}) - * @Config\Template("MarelloSupplierBundle:Supplier:widget/updateAddress.html.twig") + * @Route( + * path="/update/address/{id}", + * methods={"GET", "POST"}, + * requirements={"id"="\d+"}, + * name="marello_supplier_supplier_updateaddress" + * ) + * @Template("MarelloSupplierBundle:Supplier:widget/updateAddress.html.twig") * @AclAncestor("marello_supplier_update") * * @param Request $request @@ -163,8 +178,11 @@ public function updateAddressAction(Request $request, MarelloAddress $address) } /** - * @Config\Route("/get-supplier-default-data", name="marello_supplier_supplier_get_default_data") - * @Config\Method({"GET"}) + * @Route( + * path="/get-supplier-default-data", + * methods={"GET"}, + * name="marello_supplier_supplier_get_default_data" + * ) * @AclAncestor("marello_supplier_view") * * {@inheritdoc} diff --git a/src/Marello/Bundle/SupplierBundle/EventListener/Doctrine/SupplierDropshipEventListener.php b/src/Marello/Bundle/SupplierBundle/EventListener/Doctrine/SupplierDropshipEventListener.php index 67aab76cf..6b5d124e6 100644 --- a/src/Marello/Bundle/SupplierBundle/EventListener/Doctrine/SupplierDropshipEventListener.php +++ b/src/Marello/Bundle/SupplierBundle/EventListener/Doctrine/SupplierDropshipEventListener.php @@ -62,4 +62,18 @@ public function preUpdate(PreUpdateEventArgs $args) } } } + + /** + * @param LifecycleEventArgs $args + */ + public function preRemove(LifecycleEventArgs $args) + { + $entity = $args->getEntity(); + if ($entity instanceof Supplier && $entity->getCanDropship() === true) { + $this->eventDispatcher->dispatch( + SupplierDropshipEvent::NAME, + new SupplierDropshipEvent($entity, false) + ); + } + } } diff --git a/src/Marello/Bundle/SupplierBundle/Migrations/Data/ORM/UpdateCurrentSupplierWithCurrency.php b/src/Marello/Bundle/SupplierBundle/Migrations/Data/ORM/UpdateCurrentSupplierWithCurrency.php index 186519522..068ab926a 100644 --- a/src/Marello/Bundle/SupplierBundle/Migrations/Data/ORM/UpdateCurrentSupplierWithCurrency.php +++ b/src/Marello/Bundle/SupplierBundle/Migrations/Data/ORM/UpdateCurrentSupplierWithCurrency.php @@ -4,10 +4,12 @@ use Doctrine\Common\Persistence\ObjectManager; use Marello\Bundle\SupplierBundle\Entity\Supplier; -use Symfony\Bridge\Doctrine\Tests\Fixtures\ContainerAwareFixture; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; -class UpdateCurrentSupplierWithCurrency extends ContainerAwareFixture +class UpdateCurrentSupplierWithCurrency { + use ContainerAwareTrait; + /** * @var ObjectManager */ diff --git a/src/Marello/Bundle/SupplierBundle/Provider/SupplierProvider.php b/src/Marello/Bundle/SupplierBundle/Provider/SupplierProvider.php index 4cbf29230..220d6cdbe 100644 --- a/src/Marello/Bundle/SupplierBundle/Provider/SupplierProvider.php +++ b/src/Marello/Bundle/SupplierBundle/Provider/SupplierProvider.php @@ -7,19 +7,30 @@ use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\ProductBundle\Entity\ProductSupplierRelation; use Marello\Bundle\SupplierBundle\Entity\Supplier; +use Oro\Bundle\CurrencyBundle\Model\LocaleSettings; +use Oro\Bundle\LocaleBundle\Twig\NumberExtension; class SupplierProvider { - /** @var ObjectManager $manager */ + /** + * @var ObjectManager + */ protected $manager; + /** + * @var LocaleSettings + */ + protected $localeSettings; + /** * SupplierProvider constructor. * @param ObjectManager $manager + * @param LocaleSettings $localeSettings */ - public function __construct(ObjectManager $manager) + public function __construct(ObjectManager $manager, LocaleSettings $localeSettings) { $this->manager = $manager; + $this->localeSettings = $localeSettings; } /** @@ -55,7 +66,8 @@ public function getSupplierDefaultDataById($supplierId) return [ 'name' => $supplier->getName(), 'priority' => $supplier->getPriority(), - 'canDropship' => $supplier->getCanDropship() + 'canDropship' => $supplier->getCanDropship(), + 'currency' => $this->localeSettings->getCurrencySymbolByCurrency($supplier->getCurrency()) ]; } } diff --git a/src/Marello/Bundle/SupplierBundle/Resources/config/form.yml b/src/Marello/Bundle/SupplierBundle/Resources/config/form.yml index b8d6cf8ef..4e65560fe 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/config/form.yml +++ b/src/Marello/Bundle/SupplierBundle/Resources/config/form.yml @@ -19,7 +19,7 @@ services: ## handlers marello_supplier.form.handler.supplier: class: Marello\Bundle\SupplierBundle\Form\Handler\SupplierHandler - scope: request + public: true arguments: - '@marello_supplier.supplier.form' - '@request_stack' diff --git a/src/Marello/Bundle/SupplierBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/SupplierBundle/Resources/config/jsmodules.yml new file mode 100644 index 000000000..2819e8ff8 --- /dev/null +++ b/src/Marello/Bundle/SupplierBundle/Resources/config/jsmodules.yml @@ -0,0 +1,4 @@ +dynamic-imports: + marellosupplier: + - marellosupplier/js/app/views/product-supplier-view + - marellosupplier/js/app/views/product-suppliers-view diff --git a/src/Marello/Bundle/SupplierBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/SupplierBundle/Resources/config/oro/datagrids.yml index e6e35e317..c5f979a2e 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/SupplierBundle/Resources/config/oro/datagrids.yml @@ -39,7 +39,7 @@ datagrids: currency: data_name: s.currency default: - priority: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + priority: "ASC" filters: columns: name: @@ -105,7 +105,7 @@ datagrids: query: select: - p.id - - p.name + - p.denormalizedDefaultName - p.sku - psr.quantityOfUnit - psr.cost @@ -126,8 +126,8 @@ datagrids: label: marello.product.sku.label frontend_type: string name: - data_name: name - label: marello.product.name.label + data_name: denormalizedDefaultName + label: marello.product.names.label frontend_type: string quantityOfUnit: data_name: quantityOfUnit @@ -142,13 +142,13 @@ datagrids: sku: data_name: p.sku name: - data_name: p.name + data_name: p.denormalizedDefaultName quantityOfUnit: data_name: psr.quantityOfUnit cost: data_name: psr.cost default: - sku: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + sku: "ASC" filters: columns: sku: @@ -156,7 +156,7 @@ datagrids: data_name: p.sku name: type: string - data_name: p.name + data_name: p.denormalizedDefaultName quantityOfUnit: type: string data_name: psr.quantityOfUnit diff --git a/src/Marello/Bundle/SupplierBundle/Resources/config/oro/twig.yml b/src/Marello/Bundle/SupplierBundle/Resources/config/oro/twig.yml new file mode 100644 index 000000000..5d341df99 --- /dev/null +++ b/src/Marello/Bundle/SupplierBundle/Resources/config/oro/twig.yml @@ -0,0 +1,2 @@ +bundles: + - MarelloSupplierBundle:Form:fields.html.twig diff --git a/src/Marello/Bundle/SupplierBundle/Resources/config/services.yml b/src/Marello/Bundle/SupplierBundle/Resources/config/services.yml index 409a43984..d0d993d2e 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/SupplierBundle/Resources/config/services.yml @@ -1,20 +1,19 @@ -parameters: - marello_supplier.supplier.entity.class: Marello\Bundle\SupplierBundle\Entity\Supplier - services: # Autocomplete search handler marello_supplier.supplier.form.autocomplete.search_handler: parent: oro_form.autocomplete.search_handler arguments: - - '%marello_supplier.supplier.entity.class%' - - ["name"] + - 'Marello\Bundle\SupplierBundle\Entity\Supplier' + - ['name'] tags: - { name: oro_form.autocomplete.search_handler, alias: suppliers, acl_resource: marello_product_view } marello_supplier.provider.supplier: class: Marello\Bundle\SupplierBundle\Provider\SupplierProvider + public: true arguments: - '@doctrine.orm.entity_manager' + - '@oro_currency.locale_settings' marello_supplier.twig.supplier_extension: class: Marello\Bundle\SupplierBundle\Twig\SupplierExtension @@ -34,4 +33,5 @@ services: - '@event_dispatcher' tags: - { name: doctrine.event_listener, event: prePersist } - - { name: doctrine.event_listener, event: preUpdate } \ No newline at end of file + - { name: doctrine.event_listener, event: preUpdate } + - { name: doctrine.event_listener, event: preRemove } \ No newline at end of file diff --git a/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-supplier-view.js b/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-supplier-view.js index ca5884085..b9db258b4 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-supplier-view.js +++ b/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-supplier-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var ProductSupplierView, + const $ = require('jquery'), _ = require('underscore'), mediator = require('oroui/js/mediator'), @@ -12,7 +12,7 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemView * @class marellosupplier.app.views.ProductSupplierView */ - ProductSupplierView = AbstractItemView.extend({ + const ProductSupplierView = AbstractItemView.extend({ options: { priority: 0, canDropship: false @@ -78,11 +78,21 @@ define(function(require) { this.options.canDropship = data.canDropship; } + if (data.currency.length !== 0) { + this.options.currency = ' ' + data.currency; + } + this.fieldsByName.priority .val(this.options.priority); this.fieldsByName.canDropship .prop('checked', this.options.canDropship); + + var parent = $(this.fieldsByName.cost).parent(); + parent.contents().filter(function(){ + return (this.nodeType == 3); + }).remove(); + parent.append(this.options.currency); }, /** diff --git a/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-suppliers-view.js b/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-suppliers-view.js index dfd306f62..f88ac7258 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-suppliers-view.js +++ b/src/Marello/Bundle/SupplierBundle/Resources/public/js/app/views/product-suppliers-view.js @@ -1,7 +1,7 @@ define(function(require) { 'use strict'; - var ProductSuppliersView, + const $ = require('jquery'), _ = require('underscore'), routing = require('routing'), @@ -13,13 +13,13 @@ define(function(require) { * @extends marellolayout.app.views.AbstractItemsView * @class marellosupplier.app.views.ProductSuppliersView */ - ProductSuppliersView = AbstractItemsView.extend({ + const ProductSuppliersView = AbstractItemsView.extend({ /** * @property {Object} */ options: { suppliers: {}, - supplierDataRoute: 'marello_supplier_supplier_get_default_data', + supplierDataRoute: 'marello_supplier_supplier_get_default_data' }, /** diff --git a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/create.html.twig b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/create.html.twig index ba161a25c..9852690f7 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/create.html.twig +++ b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/create.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_supplier_supplier_update', { 'id': form.vars.value.id }) %} diff --git a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/update.html.twig b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/update.html.twig index 7b8dde849..1921f34f3 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/update.html.twig +++ b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_supplier_supplier_update', { 'id': form.vars.value.id }) %} diff --git a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/view.html.twig b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/view.html.twig index c5103a646..ef4544177 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/view.html.twig +++ b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/view.html.twig @@ -16,7 +16,7 @@ {% block navButtons %} {{ parent() }} - {% if resource_granted('EDIT', entity) %} + {% if is_granted('EDIT', entity) %} {{ UI.buttonSeparator() }} {{ UI.editButton({ 'path': path('marello_supplier_supplier_update', {'id': entity.id}), @@ -43,7 +43,7 @@ {{ UI.renderProperty('marello.supplier.supplier_is_active.label'|trans, entity.isActive ? 'marello.supplier.supplier_is_active.value.yes'|trans : 'marello.supplier.supplier_is_active.value.no'|trans) }} {{ UI.renderProperty('marello.supplier.currency.label'|trans, entity.currency) }} {{ UI.renderProperty('marello.supplier.po_send_by.label'|trans, entity.poSendBy) }} - {% set supplierViewGranted = resource_granted('marello_supplier_update') %} + {% set supplierViewGranted = is_granted('marello_supplier_update') %} {%- if supplierViewGranted -%} {%- set supplierData -%} diff --git a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/address.html.twig b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/address.html.twig index bafcacbd7..7b0f1a522 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/address.html.twig +++ b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/address.html.twig @@ -1,35 +1,29 @@ {% import 'OroUIBundle::macros.html.twig' as UI %} {% import 'MarelloAddressBundle::macros.html.twig' as address %} {% set label = 'marello.supplier.supplier_address.label' %} -
+
- {% if marello_supplier_supplier_address is not defined or resource_granted(marello_supplier_supplier_address) %} + {% if marello_supplier_supplier_address is not defined or is_granted(marello_supplier_supplier_address) %}
{% endif %} -
{{ UI.renderHtmlProperty(label|trans, address.renderAddress(supplierAddress)) }} {{ UI.renderProperty('marello.order.address.phone.label'|trans, supplierAddress.phone) }}
-
\ No newline at end of file diff --git a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/updateAddress.html.twig b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/updateAddress.html.twig index 2d31c2695..ecb08078a 100644 --- a/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/updateAddress.html.twig +++ b/src/Marello/Bundle/SupplierBundle/Resources/views/Supplier/widget/updateAddress.html.twig @@ -1,23 +1,25 @@ {% form_theme form with ['OroFormBundle:Form:fields.html.twig', _self] %} -
- {% if saved %} - - {% else %} +{% if saved %} + {% set widgetResponse = { + widget: { + trigger: [{ + eventBroker: 'widget', + name: 'formSave', + args: [form.vars.value.id], + }], + } + } %} + + {{ widgetResponse|json_encode|raw }} +{% else %} +
{% if not form.vars.valid and form_errors(form) %} -
-
- {{ form_errors(form) }} +
+
+ {{ form_errors(form) }} +
-
{% endif %} -
@@ -30,5 +32,5 @@
{{ oro_form_js_validation(form) }} - {% endif %} -
+
+{% endif %} diff --git a/src/Marello/Bundle/SupplierBundle/Tests/Functional/Controller/SupplierControllerTest.php b/src/Marello/Bundle/SupplierBundle/Tests/Functional/Controller/SupplierControllerTest.php index f2cb45238..17b2556bf 100644 --- a/src/Marello/Bundle/SupplierBundle/Tests/Functional/Controller/SupplierControllerTest.php +++ b/src/Marello/Bundle/SupplierBundle/Tests/Functional/Controller/SupplierControllerTest.php @@ -113,14 +113,14 @@ public function testLinkedProductToSupplier() 'marello-supplier-products-grid', [ 'marello-supplier-products-grid[supplierId]' => $supplier->getId(), - 'marello-supplier-products-grid[_filter][name][value]' => $product->getName() + 'marello-supplier-products-grid[_filter][name][value]' => $product->getDenormalizedDefaultName() ] ); $result = $this->getJsonResponseContent($response, Response::HTTP_OK); $result = reset($result['data']); - $this->assertContains($product->getName(), $result['name']); + $this->assertContains($product->getDenormalizedDefaultName(), $result['name']); $this->assertContains($product->getSku(), $result['sku']); } diff --git a/src/Marello/Bundle/SupplierBundle/Twig/SupplierExtension.php b/src/Marello/Bundle/SupplierBundle/Twig/SupplierExtension.php index 1270e4367..fd355be60 100644 --- a/src/Marello/Bundle/SupplierBundle/Twig/SupplierExtension.php +++ b/src/Marello/Bundle/SupplierBundle/Twig/SupplierExtension.php @@ -4,8 +4,10 @@ use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\SupplierBundle\Provider\SupplierProvider; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; -class SupplierExtension extends \Twig_Extension +class SupplierExtension extends AbstractExtension { const NAME = 'marello_supplier'; @@ -40,7 +42,7 @@ public function getName() public function getFunctions() { return [ - new \Twig_SimpleFunction( + new TwigFunction( 'marello_supplier_get_supplier_ids', [$this, 'getSuppliersIds'] ), diff --git a/src/Marello/Bundle/TaxBundle/Controller/TaxCodeController.php b/src/Marello/Bundle/TaxBundle/Controller/TaxCodeController.php index b0150b526..9fbb7543b 100644 --- a/src/Marello/Bundle/TaxBundle/Controller/TaxCodeController.php +++ b/src/Marello/Bundle/TaxBundle/Controller/TaxCodeController.php @@ -5,18 +5,22 @@ use Marello\Bundle\TaxBundle\Entity\TaxCode; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Oro\Bundle\SecurityBundle\Annotation\Acl; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Routing\Annotation\Route; /** * Class TaxCodeController * @package Marello\Bundle\TaxBundle\Controller */ -class TaxCodeController extends Controller +class TaxCodeController extends AbstractController { /** - * @Config\Route("/", name="marello_tax_taxcode_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_tax_taxcode_index" + * ) + * @Template * @AclAncestor("marello_tax_taxcode_view") */ public function indexAction() @@ -25,8 +29,12 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_tax_taxcode_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_tax_taxcode_view" + * ) + * @Template * @Acl( * id="marello_tax_taxcode_view", * type="entity", @@ -44,9 +52,12 @@ public function viewAction(TaxCode $taxCode) } /** - * @Config\Route("/create", name="marello_tax_taxcode_create") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/create", + * methods={"GET", "POST"}, + * name="marello_tax_taxcode_create" + * ) + * @Template * @Acl( * id="marello_tax_taxcode_create", * type="entity", @@ -62,9 +73,13 @@ public function createAction() } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_tax_taxcode_update") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/update/{id}", + * methods={"GET", "POST"}, + * requirements={"id"="\d+"}, + * name="marello_tax_taxcode_update" + * ) + * @Template * @Acl( * id="marello_tax_taxcode_update", * type="entity", diff --git a/src/Marello/Bundle/TaxBundle/Controller/TaxJurisdictionController.php b/src/Marello/Bundle/TaxBundle/Controller/TaxJurisdictionController.php index 08a30b3a8..f936434df 100755 --- a/src/Marello/Bundle/TaxBundle/Controller/TaxJurisdictionController.php +++ b/src/Marello/Bundle/TaxBundle/Controller/TaxJurisdictionController.php @@ -6,20 +6,24 @@ use Marello\Bundle\TaxBundle\Form\Type\TaxJurisdictionType; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Oro\Bundle\SecurityBundle\Annotation\Acl; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Annotation\Route; /** * Class TaxJurisdictionController * @package Marello\Bundle\TaxBundle\Controller */ -class TaxJurisdictionController extends Controller +class TaxJurisdictionController extends AbstractController { /** - * @Config\Route("/", name="marello_tax_taxjurisdiction_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_tax_taxjurisdiction_index" + * ) + * @Template * @AclAncestor("marello_tax_taxjurisdiction_view") * * @return array @@ -32,8 +36,12 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", name="marello_tax_taxjurisdiction_view", requirements={"id"="\d+"}) - * @Config\Template + * @Route( + * path="/view/{id}", + * name="marello_tax_taxjurisdiction_view", + * requirements={"id"="\d+"} + * ) + * @Template * @Acl( * id="marello_tax_taxjurisdiction_view", * type="entity", @@ -52,8 +60,11 @@ public function viewAction(TaxJurisdiction $taxJurisdiction) } /** - * @Config\Route("/create", name="marello_tax_taxjurisdiction_create") - * @Config\Template("MarelloTaxBundle:TaxJurisdiction:update.html.twig") + * @Route( + * path="/create", + * name="marello_tax_taxjurisdiction_create" + * ) + * @Template("MarelloTaxBundle:TaxJurisdiction:update.html.twig") * @Acl( * id="marello_tax_taxjurisdiction_create", * type="entity", @@ -70,8 +81,12 @@ public function createAction(Request $request) } /** - * @Config\Route("/update/{id}", name="marello_tax_taxjurisdiction_update", requirements={"id"="\d+"}) - * @Config\Template + * @Route( + * path="/update/{id}", + * name="marello_tax_taxjurisdiction_update", + * requirements={"id"="\d+"} + * ) + * @Template * @Acl( * id="marello_tax_taxjurisdiction_update", * type="entity", diff --git a/src/Marello/Bundle/TaxBundle/Controller/TaxRateController.php b/src/Marello/Bundle/TaxBundle/Controller/TaxRateController.php index 8c442fd6d..c474ca233 100644 --- a/src/Marello/Bundle/TaxBundle/Controller/TaxRateController.php +++ b/src/Marello/Bundle/TaxBundle/Controller/TaxRateController.php @@ -5,18 +5,22 @@ use Marello\Bundle\TaxBundle\Entity\TaxRate; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; use Oro\Bundle\SecurityBundle\Annotation\Acl; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Routing\Annotation\Route; /** * Class TaxRateController * @package Marello\Bundle\TaxBundle\Controller */ -class TaxRateController extends Controller +class TaxRateController extends AbstractController { /** - * @Config\Route("/", name="marello_tax_taxrate_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_tax_taxrate_index" + * ) + * @Template * @AclAncestor("marello_tax_taxrate_view") */ public function indexAction() @@ -25,8 +29,12 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_tax_taxrate_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_tax_taxrate_view" + * ) + * @Template * @Acl( * id="marello_tax_taxrate_view", * type="entity", @@ -44,9 +52,12 @@ public function viewAction(TaxRate $taxRate) } /** - * @Config\Route("/create", name="marello_tax_taxrate_create") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/create", + * methods={"GET", "POST"}, + * name="marello_tax_taxrate_create" + * ) + * @Template * @Acl( * id="marello_tax_taxrate_create", * type="entity", @@ -62,9 +73,13 @@ public function createAction() } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_tax_taxrate_update") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/update/{id}", + * methods={"GET", "POST"}, + * requirements={"id"="\d+"}, + * name="marello_tax_taxrate_update" + * ) + * @Template * @Acl( * id="marello_tax_taxrate_update", * type="entity", diff --git a/src/Marello/Bundle/TaxBundle/Controller/TaxRuleController.php b/src/Marello/Bundle/TaxBundle/Controller/TaxRuleController.php index b4ca93b52..b6e764edf 100644 --- a/src/Marello/Bundle/TaxBundle/Controller/TaxRuleController.php +++ b/src/Marello/Bundle/TaxBundle/Controller/TaxRuleController.php @@ -5,18 +5,22 @@ use Marello\Bundle\TaxBundle\Entity\TaxRule; use Oro\Bundle\SecurityBundle\Annotation\Acl; use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; -use Sensio\Bundle\FrameworkExtraBundle\Configuration as Config; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Routing\Annotation\Route; /** * Class TaxRuleController * @package Marello\Bundle\TaxBundle\Controller */ -class TaxRuleController extends Controller +class TaxRuleController extends AbstractController { /** - * @Config\Route("/", name="marello_tax_taxrule_index") - * @Config\Template + * @Route( + * path="/", + * name="marello_tax_taxrule_index" + * ) + * @Template * @AclAncestor("marello_tax_taxrule_view") */ public function indexAction() @@ -25,8 +29,12 @@ public function indexAction() } /** - * @Config\Route("/view/{id}", requirements={"id"="\d+"}, name="marello_tax_taxrule_view") - * @Config\Template + * @Route( + * path="/view/{id}", + * requirements={"id"="\d+"}, + * name="marello_tax_taxrule_view" + * ) + * @Template * @Acl( * id="marello_tax_taxrule_view", * type="entity", @@ -44,9 +52,12 @@ public function viewAction(TaxRule $taxRule) } /** - * @Config\Route("/create", name="marello_tax_taxrule_create") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/create", + * methods={"GET", "POST"}, + * name="marello_tax_taxrule_create" + * ) + * @Template * @Acl( * id="marello_tax_taxrule_create", * type="entity", @@ -62,9 +73,13 @@ public function createAction() } /** - * @Config\Route("/update/{id}", requirements={"id"="\d+"}, name="marello_tax_taxrule_update") - * @Config\Method({"GET", "POST"}) - * @Config\Template + * @Route( + * path="/update/{id}", + * methods={"GET", "POST"}, + * requirements={"id"="\d+"}, + * name="marello_tax_taxrule_update" + * ) + * @Template * @Acl( * id="marello_tax_taxrule_update", * type="entity", diff --git a/src/Marello/Bundle/TaxBundle/DependencyInjection/MarelloTaxExtension.php b/src/Marello/Bundle/TaxBundle/DependencyInjection/MarelloTaxExtension.php index 02467064f..09651c03c 100644 --- a/src/Marello/Bundle/TaxBundle/DependencyInjection/MarelloTaxExtension.php +++ b/src/Marello/Bundle/TaxBundle/DependencyInjection/MarelloTaxExtension.php @@ -15,7 +15,7 @@ class MarelloTaxExtension extends Extension public function load(array $config, ContainerBuilder $container) { $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - $loader->load('service.yml'); + $loader->load('services.yml'); $loader->load('form.yml'); } } diff --git a/src/Marello/Bundle/TaxBundle/Entity/ZipCode.php b/src/Marello/Bundle/TaxBundle/Entity/ZipCode.php index 14ff0d0af..3552e432d 100755 --- a/src/Marello/Bundle/TaxBundle/Entity/ZipCode.php +++ b/src/Marello/Bundle/TaxBundle/Entity/ZipCode.php @@ -6,7 +6,6 @@ use Oro\Bundle\EntityBundle\EntityProperty\DatesAwareInterface; use Oro\Bundle\EntityBundle\EntityProperty\DatesAwareTrait; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\Config; -use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\ConfigField; /** * @ORM\Entity diff --git a/src/Marello/Bundle/TaxBundle/EventListener/Datagrid/TaxCodeDatagridListener.php b/src/Marello/Bundle/TaxBundle/EventListener/Datagrid/TaxCodeDatagridListener.php new file mode 100644 index 000000000..61abc597a --- /dev/null +++ b/src/Marello/Bundle/TaxBundle/EventListener/Datagrid/TaxCodeDatagridListener.php @@ -0,0 +1,152 @@ +relatedEntityClass = $relatedEntityClass; + + $this->expressionBuilder = new Expr(); + } + + /** + * @param BuildBefore $event + */ + public function onBuildBefore(BuildBefore $event) + { + $config = $event->getConfig(); + + $this->addSelect($config); + $this->addJoin($config); + $this->addColumn($config); + $this->addSorter($config); + $this->addFilter($config); + } + + /** + * @param DatagridConfiguration $configuration + * @return string + * @throws \InvalidArgumentException when a root entity not found in the grid + */ + protected function getAlias(DatagridConfiguration $configuration) + { + $rootAlias = $configuration->getOrmQuery()->getRootAlias(); + if (!$rootAlias) { + throw new \InvalidArgumentException( + sprintf( + 'A root entity is missing for grid "%s"', + $configuration->getName() + ) + ); + } + + return $rootAlias; + } + + /** + * @return string + */ + protected function getColumnLabel() + { + return 'marello.tax.taxcode.entity_label'; + } + + /** + * @return string + */ + protected function getDataName() + { + return self::DATA_NAME; + } + + /** + * @return string + */ + protected function getJoinAlias() + { + return self::JOIN_ALIAS; + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSelect(DatagridConfiguration $config) + { + $config->getOrmQuery()->addSelect( + sprintf('%s.code AS %s', $this->getJoinAlias(), $this->getDataName()) + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addJoin(DatagridConfiguration $config) + { + $config->getOrmQuery()->addLeftJoin( + $this->getAlias($config).'.taxCode', + $this->getJoinAlias() + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addColumn(DatagridConfiguration $config) + { + $config->offsetSetByPath(sprintf('[columns][%s]', $this->getDataName()), ['label' => $this->getColumnLabel()]); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addSorter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[sorters][columns][%s]', $this->getDataName()), + ['data_name' => $this->getDataName()] + ); + } + + /** + * @param DatagridConfiguration $config + */ + protected function addFilter(DatagridConfiguration $config) + { + $config->offsetSetByPath( + sprintf('[filters][columns][%s]', $this->getDataName()), + [ + 'type' => 'entity', + 'data_name' => $this->getAlias($config) . '.taxCode', + 'options' => [ + 'field_options' => [ + 'multiple' => true, + 'class' => $this->taxCodeClass, + 'choice_label' => 'code' + ] + ] + ] + ); + } +} diff --git a/src/Marello/Bundle/TaxBundle/Migrations/Schema/v1_3/MarelloTaxBundle.php b/src/Marello/Bundle/TaxBundle/Migrations/Schema/v1_3/MarelloTaxBundle.php index 6bd366187..85c07aa71 100644 --- a/src/Marello/Bundle/TaxBundle/Migrations/Schema/v1_3/MarelloTaxBundle.php +++ b/src/Marello/Bundle/TaxBundle/Migrations/Schema/v1_3/MarelloTaxBundle.php @@ -3,7 +3,6 @@ namespace Marello\Bundle\TaxBundle\Migrations\Schema\v1_3; use Doctrine\DBAL\Schema\Schema; -use Doctrine\DBAL\Types\Type; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; diff --git a/src/Marello/Bundle/TaxBundle/Provider/TaxCode/TaxCodesChoicesProvider.php b/src/Marello/Bundle/TaxBundle/Provider/TaxCode/TaxCodesChoicesProvider.php index fe8d7e57e..cc0516974 100755 --- a/src/Marello/Bundle/TaxBundle/Provider/TaxCode/TaxCodesChoicesProvider.php +++ b/src/Marello/Bundle/TaxBundle/Provider/TaxCode/TaxCodesChoicesProvider.php @@ -2,7 +2,6 @@ namespace Marello\Bundle\TaxBundle\Provider\TaxCode; -use Marello\Bundle\SupplierBundle\Entity\Supplier; use Marello\Bundle\TaxBundle\Entity\TaxCode; use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; diff --git a/src/Marello/Bundle/TaxBundle/Provider/TaxSubtotalProvider.php b/src/Marello/Bundle/TaxBundle/Provider/TaxSubtotalProvider.php index 279f940e0..734731879 100644 --- a/src/Marello/Bundle/TaxBundle/Provider/TaxSubtotalProvider.php +++ b/src/Marello/Bundle/TaxBundle/Provider/TaxSubtotalProvider.php @@ -8,7 +8,7 @@ use Marello\Bundle\TaxBundle\Factory\TaxFactory; use Marello\Bundle\TaxBundle\Model\Result; use Marello\Bundle\TaxBundle\Model\Taxable; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class TaxSubtotalProvider implements SubtotalProviderInterface { diff --git a/src/Marello/Bundle/TaxBundle/Resources/config/form.yml b/src/Marello/Bundle/TaxBundle/Resources/config/form.yml index 2213ce800..5b7e53959 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/config/form.yml +++ b/src/Marello/Bundle/TaxBundle/Resources/config/form.yml @@ -1,10 +1,3 @@ -parameters: - marello_tax.taxcode.entity.class: Marello\Bundle\TaxBundle\Entity\TaxCode - marello_tax.taxrate.entity.class: Marello\Bundle\TaxBundle\Entity\TaxRate - marello_tax.taxrule.entity.class: Marello\Bundle\TaxBundle\Entity\TaxRule - marello_tax.taxjurisdiction.entity.class: Marello\Bundle\TaxBundle\Entity\TaxJurisdiction - marello_tax.zipcode.entity.class: Marello\Bundle\TaxBundle\Entity\ZipCode - services: ## forms marello_tax.form.type.tax_code: @@ -58,7 +51,7 @@ services: marello_tax.taxcode.form.autocomplete.search_handler: parent: oro_form.autocomplete.search_handler arguments: - - '%marello_tax.taxcode.entity.class%' + - 'Marello\Bundle\TaxBundle\Entity\TaxCode' - ["code"] tags: - { name: oro_form.autocomplete.search_handler, alias: taxcodes, acl_resource: marello_tax_taxcode_view } @@ -66,7 +59,7 @@ services: marello_tax.taxrate.form.autocomplete.search_handler: parent: oro_form.autocomplete.search_handler arguments: - - '%marello_tax.taxrate.entity.class%' + - 'Marello\Bundle\TaxBundle\Entity\TaxRate' - ['code'] tags: - { name: oro_form.autocomplete.search_handler, alias: taxrates, acl_resource: marello_tax_taxrate_view } @@ -74,7 +67,7 @@ services: marello_tax.taxjurisdiction.form.autocomplete.search_handler: parent: oro_form.autocomplete.search_handler arguments: - - '%marello_tax.taxjurisdiction.entity.class%' + - 'Marello\Bundle\TaxBundle\Entity\TaxJurisdiction' - ['code'] tags: - { name: oro_form.autocomplete.search_handler, alias: taxjurisdictions, acl_resource: marello_tax_taxjurisdiction_view } @@ -101,7 +94,7 @@ services: ## handlers marello_tax.form.handler.taxcode: class: Marello\Bundle\TaxBundle\Form\Handler\TaxCodeHandler - scope: request + public: true arguments: - '@marello_tax.taxcode.form' - '@request_stack' @@ -109,7 +102,7 @@ services: marello_tax.form.handler.taxrate: class: Marello\Bundle\TaxBundle\Form\Handler\TaxRateHandler - scope: request + public: true arguments: - '@marello_tax.taxrate.form' - '@request_stack' @@ -117,7 +110,7 @@ services: marello_tax.form.handler.taxrule: class: Marello\Bundle\TaxBundle\Form\Handler\TaxRuleHandler - scope: request + public: true arguments: - '@marello_tax.taxrule.form' - '@request_stack' diff --git a/src/Marello/Bundle/TaxBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/TaxBundle/Resources/config/oro/datagrids.yml index dc42f38ea..8c3ade965 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/TaxBundle/Resources/config/oro/datagrids.yml @@ -21,7 +21,7 @@ datagrids: description: data_name: t.description default: - code: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + code: "ASC" filters: columns: code: @@ -69,7 +69,7 @@ datagrids: rate: data_name: t.rate default: - code: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + code: "ASC" filters: columns: code: @@ -130,7 +130,7 @@ datagrids: jurisdiction: data_name: jurisdiction default: - code: "%oro_datagrid.extension.orm_sorter.class%::DIRECTION_ASC" + code: "ASC" filters: columns: code: diff --git a/src/Marello/Bundle/TaxBundle/Resources/config/service.yml b/src/Marello/Bundle/TaxBundle/Resources/config/services.yml similarity index 86% rename from src/Marello/Bundle/TaxBundle/Resources/config/service.yml rename to src/Marello/Bundle/TaxBundle/Resources/config/services.yml index 9c1576ff4..db423a671 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/config/service.yml +++ b/src/Marello/Bundle/TaxBundle/Resources/config/services.yml @@ -1,8 +1,3 @@ -parameters: - marello_tax.entity.tax_code.class: Marello\Bundle\TaxBundle\Entity\TaxCode - marello_tax.entity.tax_rate.class: Marello\Bundle\TaxBundle\Entity\TaxRate - marello_tax.entity.tax_rule.class: Marello\Bundle\TaxBundle\Entity\TaxRule - marello_tax.entity.tax_jurisdiction.class: Marello\Bundle\TaxBundle\Entity\TaxJurisdiction services: marello_tax.tax_rule.matcher.composite: class: Marello\Bundle\TaxBundle\Matcher\CompositeTaxRuleMatcher @@ -80,8 +75,8 @@ services: class: 'Marello\Bundle\TaxBundle\Resolver\CustomerAddressItemResolver' public: false arguments: - - "@marello_tax.resolver.row_total" - - "@marello_tax.tax_rule.matcher.composite" + - '@marello_tax.resolver.row_total' + - '@marello_tax.tax_rule.matcher.composite' tags: - { name: marello_tax.resolver, event: marello_tax.resolve, priority: -32 } @@ -89,7 +84,7 @@ services: class: 'Marello\Bundle\TaxBundle\Resolver\CustomerAddressResolver' public: false arguments: - - "@marello_tax.resolver.customer_address.item" + - '@marello_tax.resolver.customer_address.item' tags: - { name: marello_tax.resolver, event: marello_tax.resolve, priority: -32 } @@ -109,13 +104,13 @@ services: class: 'Marello\Bundle\TaxBundle\Event\TaxEventDispatcher' public: false arguments: - - "@event_dispatcher" + - '@event_dispatcher' marello_tax.order_tax.mapper.order_item_mapper: class: 'Marello\Bundle\TaxBundle\OrderTax\Mapper\OrderItemMapper' public: false arguments: - - '%marello_order.entity.order_item.class%' + - 'Marello\Bundle\OrderBundle\Entity\OrderItem' tags: - { name: marello_tax.tax_mapper } @@ -123,7 +118,7 @@ services: class: 'Marello\Bundle\TaxBundle\OrderTax\Mapper\OrderMapper' public: false arguments: - - '%marello_order.entity.order.class%' + - 'Marello\Bundle\OrderBundle\Entity\Order' calls: - ['setOrderItemMapper', ['@marello_tax.order_tax.mapper.order_item_mapper']] tags: @@ -131,5 +126,13 @@ services: marello_tax.provider.tax_codes_choices: class: 'Marello\Bundle\TaxBundle\Provider\TaxCode\TaxCodesChoicesProvider' + public: true arguments: - '@oro_entity.doctrine_helper' + + marello_tax.event_listener.datagrid.products_grid: + class: 'Marello\Bundle\TaxBundle\EventListener\Datagrid\TaxCodeDatagridListener' + arguments: + - 'Marello\Bundle\ProductBundle\Entity\Product' + tags: + - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.marello-products-grid, method: onBuildBefore } \ No newline at end of file diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/Form/fields.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/Form/fields.html.twig index 311d87dcf..37fef5db9 100755 --- a/src/Marello/Bundle/TaxBundle/Resources/views/Form/fields.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/Form/fields.html.twig @@ -1,7 +1,10 @@ {% block marello_tax_zip_code_collection_type_widget %} + {% import _self as fields %} + {% spaceless %} + {% set widgetContainerAttributes = block('widget_container_attributes') %} {% if prototype is defined %} - {% set prototype_html = _self.marello_tax_zip_code_collection_prototype(form) %} + {% set prototype_html = fields.marello_tax_zip_code_collection_prototype(form, widgetContainerAttributes) %} {% endif %} {% set attr = attr|merge({'class': (attr.class is defined ? attr.class ~ ' ' : '') ~ 'oro-item-collection collection-fields-list' }) %} {% set id = id ~ '_collection' %} @@ -20,7 +23,7 @@ {% if form.children|length %} {% for child in form.children %} - {{ _self.marello_tax_zip_code_collection_prototype(child) }} + {{ fields.marello_tax_zip_code_collection_prototype(child, widgetContainerAttributes) }} {% endfor %} {% elseif show_form_when_empty and prototype_html is defined %} {% for i in 0..(form.vars.row_count_initial - 1) %} @@ -37,7 +40,7 @@ {% endspaceless %} {% endblock %} -{% macro marello_tax_zip_code_collection_prototype(widget) %} +{% macro marello_tax_zip_code_collection_prototype(widget, attributes) %} {% if 'collection' in widget.vars.block_prefixes %} {% set form = widget.vars.prototype %} {% set name = widget.vars.full_name ~ '[' ~ widget.vars.prototype.vars.name ~ ']' %} @@ -45,7 +48,7 @@ {% set form = widget %} {% set name = widget.vars.full_name %} {% endif %} - {{ form_widget(form) }} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/create.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/create.html.twig index 9a9a5845e..4ad51d8b5 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/create.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/create.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_tax_taxcode_update', { 'id': form.vars.value.id }) %} @@ -9,9 +10,9 @@ {% block navButtons %} {{ UI.cancelButton(path('marello_tax_taxcode_index')) }} - {% if resource_granted('marello_tax_taxcode_create') %} + {% if is_granted('marello_tax_taxcode_create') %} {% set html = '' %} - {% if resource_granted('marello_tax_taxcode_view') %} + {% if is_granted('marello_tax_taxcode_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_tax_taxcode_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/update.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/update.html.twig index 7344c9b59..500326969 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/update.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_tax_taxcode_update', { 'id': form.vars.value.id }) %} @@ -11,9 +12,9 @@ {{ parent() }} {{ UI.cancelButton(path('marello_tax_taxcode_index')) }} - {% if entity.id and resource_granted('marello_tax_taxcode_update') %} + {% if entity.id and is_granted('marello_tax_taxcode_update') %} {% set html = '' %} - {% if resource_granted('marello_tax_taxcode_view') %} + {% if is_granted('marello_tax_taxcode_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_tax_taxcode_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/view.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/view.html.twig index 92da230ec..4f0e82b61 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/view.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxCode/view.html.twig @@ -27,7 +27,7 @@
{{ UI.renderProperty('marello.tax.taxcode.code.label'|trans, entity.code) }} {{ UI.renderProperty('marello.tax.taxcode.description.label'|trans, entity.description) }} - {% set taxCodeViewGranted = resource_granted('marello_tax_taxcode_update') %} + {% set taxCodeViewGranted = is_granted('marello_tax_taxcode_update') %} {%- if taxCodeViewGranted -%} {%- set taxCodeData -%}
diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/index.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/index.html.twig index 081ae3916..12b9e8e68 100755 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/index.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/index.html.twig @@ -6,7 +6,7 @@ {% set pageTitle = 'marello.tax.taxjurisdiction.entity_plural_label'|trans %} {% block navButtons %} - {% if resource_granted('marello_tax_taxjurisdiction_create') %} + {% if is_granted('marello_tax_taxjurisdiction_create') %}
{{ UI.addButton({ 'path': path('marello_tax_taxjurisdiction_create'), diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/update.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/update.html.twig index 32722030a..7747902b2 100755 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/update.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/update.html.twig @@ -1,5 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} - +{% import 'OroUIBundle::macros.html.twig' as UI %} {% set formAction = entity.id ? path('marello_tax_taxjurisdiction_update', {id: entity.id}) : path('marello_tax_taxjurisdiction_create') %} {% oro_title_set({params : {"%taxCode%": entity.code|default('N/A'|trans), '%entityName%': 'marello.tax.taxjurisdiction.entity_label'|trans} }) %} @@ -8,9 +8,9 @@ {{ parent() }} {{ UI.cancelButton(path('marello_tax_taxjurisdiction_index')) }} - {% if entity.id and resource_granted('marello_tax_taxjurisdiction_update') or resource_granted('marello_tax_taxjurisdiction_create') %} + {% if entity.id and is_granted('marello_tax_taxjurisdiction_update') or is_granted('marello_tax_taxjurisdiction_create') %} {% set html = '' %} - {% if resource_granted('marello_tax_taxjurisdiction_view') %} + {% if is_granted('marello_tax_taxjurisdiction_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_tax_taxjurisdiction_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/view.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/view.html.twig index d898cca6c..51e290d07 100755 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/view.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxJurisdiction/view.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:view.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% import 'OroDataGridBundle::macros.html.twig' as dataGrid %} {% oro_title_set({params : {"%taxCode%": entity.code } }) %} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/create.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/create.html.twig index 9be9ec030..5c609bfba 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/create.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/create.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_tax_taxrate_update', { 'id': form.vars.value.id }) %} @@ -9,9 +10,9 @@ {% block navButtons %} {{ UI.cancelButton(path('marello_tax_taxrate_index')) }} - {% if resource_granted('marello_tax_taxrate_create') %} + {% if is_granted('marello_tax_taxrate_create') %} {% set html = '' %} - {% if resource_granted('marello_tax_taxrate_view') %} + {% if is_granted('marello_tax_taxrate_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_tax_taxrate_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/update.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/update.html.twig index 1c98f88df..c9b4371d8 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/update.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_tax_taxrate_update', { 'id': form.vars.value.id }) %} @@ -11,9 +12,9 @@ {{ parent() }} {{ UI.cancelButton(path('marello_tax_taxrate_index')) }} - {% if entity.id and resource_granted('marello_tax_taxrate_update') %} + {% if entity.id and is_granted('marello_tax_taxrate_update') %} {% set html = '' %} - {% if resource_granted('marello_tax_taxrate_view') %} + {% if is_granted('marello_tax_taxrate_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_tax_taxrate_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/view.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/view.html.twig index 38a0767b2..ae981ac11 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/view.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRate/view.html.twig @@ -27,7 +27,7 @@
{{ UI.renderProperty('marello.tax.taxrate.code.label'|trans, entity.code) }} {{ UI.renderProperty('marello.tax.taxrate.rate.label'|trans, entity.rate|oro_format_percent) }} - {% set taxRateViewGranted = resource_granted('marello_tax_taxrate_update') %} + {% set taxRateViewGranted = is_granted('marello_tax_taxrate_update') %} {%- if taxRateViewGranted -%} {%- set taxRateData -%}
diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/create.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/create.html.twig index 54a619e16..1ea25d47c 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/create.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/create.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_tax_taxrule_update', { 'id': form.vars.value.id }) %} @@ -9,9 +10,9 @@ {% block navButtons %} {{ UI.cancelButton(path('marello_tax_taxrule_index')) }} - {% if resource_granted('marello_tax_taxrule_create') %} + {% if is_granted('marello_tax_taxrule_create') %} {% set html = '' %} - {% if resource_granted('marello_tax_taxrule_view') %} + {% if is_granted('marello_tax_taxrule_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_tax_taxrule_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/update.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/update.html.twig index a85a23776..5df5dfe91 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/update.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/update.html.twig @@ -1,4 +1,5 @@ {% extends 'OroUIBundle:actions:update.html.twig' %} +{% import 'OroUIBundle::macros.html.twig' as UI %} {% form_theme form with 'OroFormBundle:Form:fields.html.twig' %} {% if form.vars.value.id %} {% set formAction = path('marello_tax_taxrule_update', { 'id': form.vars.value.id }) %} @@ -11,9 +12,9 @@ {{ parent() }} {{ UI.cancelButton(path('marello_tax_taxrule_index')) }} - {% if entity.id and resource_granted('marello_tax_taxrule_update') %} + {% if entity.id and is_granted('marello_tax_taxrule_update') %} {% set html = '' %} - {% if resource_granted('marello_tax_taxrule_view') %} + {% if is_granted('marello_tax_taxrule_view') %} {% set html = UI.saveAndCloseButton({ 'route': 'marello_tax_taxrule_view', 'params': {'id': '$id'} diff --git a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/view.html.twig b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/view.html.twig index 340216fb3..9d5f2fad5 100644 --- a/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/view.html.twig +++ b/src/Marello/Bundle/TaxBundle/Resources/views/TaxRule/view.html.twig @@ -28,7 +28,7 @@ {{ UI.renderProperty('marello.tax.taxrule.tax_code.label'|trans, entity.taxCode) }} {{ UI.renderHtmlProperty('marello.tax.taxrule.tax_rate.label'|trans, entity.taxRate.code ~ '(' ~ entity.taxRate.rate|oro_format_percent ~ ')' ) }} {{ UI.renderProperty('marello.tax.taxrule.tax_jurisdiction.label'|trans, entity.taxJurisdiction) }} - {% set taxRateViewGranted = resource_granted('marello_tax_taxrule_update') %} + {% set taxRateViewGranted = is_granted('marello_tax_taxrule_update') %} {%- if taxRateViewGranted -%} {%- set taxRateData -%} diff --git a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxCodeDeleteOperationTest.php b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxCodeDeleteOperationTest.php index 0c7e73852..a1183fc0e 100755 --- a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxCodeDeleteOperationTest.php +++ b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxCodeDeleteOperationTest.php @@ -3,6 +3,8 @@ namespace Marello\Bundle\TaxBundle\Tests\Functional\Operation; use Oro\Bundle\ActionBundle\Tests\Functional\ActionTestCase; + +use Marello\Bundle\TaxBundle\Entity\TaxCode; use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxCodeData; class TaxCodeDeleteOperationTest extends ActionTestCase @@ -25,7 +27,7 @@ public function testDelete() $this->assertDeleteOperation( $productTaxCode->getId(), - 'marello_tax.taxcode.entity.class', + TaxCode::class, 'marello_tax_taxcode_index' ); } diff --git a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxJurisdictionDeleteOperationTest.php b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxJurisdictionDeleteOperationTest.php index 1e2bb0f8e..197de0c94 100755 --- a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxJurisdictionDeleteOperationTest.php +++ b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxJurisdictionDeleteOperationTest.php @@ -2,10 +2,12 @@ namespace Marello\Bundle\TaxBundle\Tests\Functional\Operation; -use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxJurisdictionData; -use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxRateData; use Oro\Bundle\ActionBundle\Tests\Functional\ActionTestCase; +use Marello\Bundle\TaxBundle\Entity\TaxJurisdiction; +use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxRateData; +use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxJurisdictionData; + class TaxJurisdictionDeleteOperationTest extends ActionTestCase { protected function setUp() @@ -28,7 +30,7 @@ public function testDelete() $this->assertDeleteOperation( $taxJurisdiction->getId(), - 'marello_tax.taxjurisdiction.entity.class', + TaxJurisdiction::class, 'marello_tax_taxjurisdiction_index' ); } diff --git a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRateDeleteOperationTest.php b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRateDeleteOperationTest.php index 7916b22a7..fde779d29 100755 --- a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRateDeleteOperationTest.php +++ b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRateDeleteOperationTest.php @@ -3,6 +3,8 @@ namespace Marello\Bundle\TaxBundle\Tests\Functional\Operation; use Oro\Bundle\ActionBundle\Tests\Functional\ActionTestCase; + +use Marello\Bundle\TaxBundle\Entity\TaxRate; use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxRateData; class TaxRateDeleteOperationTest extends ActionTestCase @@ -25,7 +27,7 @@ public function testDelete() $this->assertDeleteOperation( $taxRate->getId(), - 'marello_tax.taxrate.entity.class', + TaxRate::class, 'marello_tax_taxrate_index' ); } diff --git a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRuleDeleteOperationTest.php b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRuleDeleteOperationTest.php index 0efafc8ed..5ef1ef5b2 100755 --- a/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRuleDeleteOperationTest.php +++ b/src/Marello/Bundle/TaxBundle/Tests/Functional/Operation/TaxRuleDeleteOperationTest.php @@ -2,9 +2,11 @@ namespace Marello\Bundle\TaxBundle\Tests\Functional\Operation; -use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxRuleData; use Oro\Bundle\ActionBundle\Tests\Functional\ActionTestCase; +use Marello\Bundle\TaxBundle\Entity\TaxRule; +use Marello\Bundle\TaxBundle\Tests\Functional\DataFixtures\LoadTaxRuleData; + class TaxRuleDeleteOperationTest extends ActionTestCase { protected function setUp() @@ -27,7 +29,7 @@ public function testDelete() $this->assertDeleteOperation( $taxRule->getId(), - 'marello_tax.taxrule.entity.class', + TaxRule::class, 'marello_tax_taxrule_index' ); } diff --git a/src/Marello/Bundle/TaxBundle/Tests/Unit/Calculator/ExcludedTaxCalculatorTest.php b/src/Marello/Bundle/TaxBundle/Tests/Unit/Calculator/ExcludedTaxCalculatorTest.php index 8e59110c4..725cc8ff5 100644 --- a/src/Marello/Bundle/TaxBundle/Tests/Unit/Calculator/ExcludedTaxCalculatorTest.php +++ b/src/Marello/Bundle/TaxBundle/Tests/Unit/Calculator/ExcludedTaxCalculatorTest.php @@ -23,10 +23,10 @@ public function calculateDataProvider() // edge cases [['31.96', '15.98', '15.98'], '15.98', '1'], [['47.94', '15.98', '31.96'], '15.98', '2'], - [['31.80', '15.98', '15.82'], '15.98', '0.99'], - [['16.00', '15.98', '0.02'], '15.98', '0.001'], - [['16.00', '15.98', '0.02'], '15.98', '0.0015'], - [['19.18', '15.98', '3.20'], '15.98', '-0.2'], + [['31.8', '15.98', '15.82'], '15.98', '0.99'], + [['16', '15.98', '0.02'], '15.98', '0.001'], + [['16', '15.98', '0.02'], '15.98', '0.0015'], + [['19.18', '15.98', '3.2'], '15.98', '-0.2'], ]; } diff --git a/src/Marello/Bundle/TaxBundle/Tests/Unit/Provider/TaxSubtotalProviderTest.php b/src/Marello/Bundle/TaxBundle/Tests/Unit/Provider/TaxSubtotalProviderTest.php index 6a788f78e..194ae112f 100644 --- a/src/Marello/Bundle/TaxBundle/Tests/Unit/Provider/TaxSubtotalProviderTest.php +++ b/src/Marello/Bundle/TaxBundle/Tests/Unit/Provider/TaxSubtotalProviderTest.php @@ -2,7 +2,7 @@ namespace Marello\Bundle\TaxBundle\Tests\Unit\Provider; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use PHPUnit\Framework\TestCase; diff --git a/src/Marello/Bundle/TaxBundle/Tests/Unit/Resolver/TotalResolverTest.php b/src/Marello/Bundle/TaxBundle/Tests/Unit/Resolver/TotalResolverTest.php index 2500ef25d..e2d6cfb01 100644 --- a/src/Marello/Bundle/TaxBundle/Tests/Unit/Resolver/TotalResolverTest.php +++ b/src/Marello/Bundle/TaxBundle/Tests/Unit/Resolver/TotalResolverTest.php @@ -117,11 +117,11 @@ public function resolveDataProvider() 'no shipping taxes' => [ 'items' => [ [ - Result::ROW => ResultElement::create('21.50', '20.00', '1.50'), + Result::ROW => ResultElement::create('21.5', '20', '1.5'), ], ], 'shippingResult' => null, - 'expectedTotalResult' => ResultElement::create('21.50', '20.00', '1.50'), + 'expectedTotalResult' => ResultElement::create('21.5', '20', '1.5'), ], ]; } diff --git a/src/Marello/Bundle/UPSBundle/Controller/AjaxUPSController.php b/src/Marello/Bundle/UPSBundle/Controller/AjaxUPSController.php index bee645245..60a1485c9 100644 --- a/src/Marello/Bundle/UPSBundle/Controller/AjaxUPSController.php +++ b/src/Marello/Bundle/UPSBundle/Controller/AjaxUPSController.php @@ -9,21 +9,22 @@ use Marello\Bundle\UPSBundle\Entity\Repository\ShippingServiceRepository; use Marello\Bundle\UPSBundle\Entity\UPSSettings; use Oro\Bundle\IntegrationBundle\Form\Type\ChannelType; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -class AjaxUPSController extends Controller +class AjaxUPSController extends AbstractController { /** - * @Route("/get-shipping-services-by-country/{code}", - * name="marello_ups_country_shipping_services", - * requirements={"code"="^[A-Z]{2}$"}) + * @Route( + * path="/get-shipping-services-by-country/{code}", + * methods={"GET"}, + * name="marello_ups_country_shipping_services", + * requirements={"code"="^[A-Z]{2}$"} + * ) * @ParamConverter("country", options={"id" = "code"}) - * @Method("GET") * @param Country $country * @return JsonResponse */ @@ -43,9 +44,12 @@ public function getShippingServicesByCountryAction(Country $country) } /** - * @Route("/validate-connection/{channelId}/", name="marello_ups_validate_connection") + * @Route( + * path="/validate-connection/{channelId}/", + * methods={"POST"}, + * name="marello_ups_validate_connection" + * ) * @ParamConverter("channel", class="OroIntegrationBundle:Channel", options={"id" = "channelId"}) - * @Method("POST") * * @param Request $request * @param Channel|null $channel diff --git a/src/Marello/Bundle/UPSBundle/Migrations/Data/ORM/Config/ChannelByTypeFactory.php b/src/Marello/Bundle/UPSBundle/Migrations/Data/ORM/Config/ChannelByTypeFactory.php index ef87990d2..74c61e923 100755 --- a/src/Marello/Bundle/UPSBundle/Migrations/Data/ORM/Config/ChannelByTypeFactory.php +++ b/src/Marello/Bundle/UPSBundle/Migrations/Data/ORM/Config/ChannelByTypeFactory.php @@ -6,7 +6,7 @@ use Marello\Bundle\UPSBundle\Provider\ChannelType; use Oro\Bundle\IntegrationBundle\Entity\Channel; use Oro\Bundle\OrganizationBundle\Entity\OrganizationInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class ChannelByTypeFactory { diff --git a/src/Marello/Bundle/UPSBundle/Resources/config/jsmodules.yml b/src/Marello/Bundle/UPSBundle/Resources/config/jsmodules.yml new file mode 100644 index 000000000..3303c13e7 --- /dev/null +++ b/src/Marello/Bundle/UPSBundle/Resources/config/jsmodules.yml @@ -0,0 +1,3 @@ +dynamic-imports: + marelloups: + - marelloups/js/app/components/ups-transport-settings-component \ No newline at end of file diff --git a/src/Marello/Bundle/UPSBundle/Resources/config/requirejs.yml b/src/Marello/Bundle/UPSBundle/Resources/config/requirejs.yml deleted file mode 100644 index b91462d5a..000000000 --- a/src/Marello/Bundle/UPSBundle/Resources/config/requirejs.yml +++ /dev/null @@ -1,3 +0,0 @@ -config: - paths: - 'marelloups/js/app/components/ups-transport-settings-component': 'bundles/marelloups/js/app/components/ups-transport-settings-component.js' diff --git a/src/Marello/Bundle/UPSBundle/Resources/config/services.yml b/src/Marello/Bundle/UPSBundle/Resources/config/services.yml index f0455d997..51076f5b6 100644 --- a/src/Marello/Bundle/UPSBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/UPSBundle/Resources/config/services.yml @@ -34,6 +34,7 @@ services: marello_ups.method.identifier_generator.method: parent: oro_integration.generator.prefixed_identifier_generator + public: true arguments: - '%marello_ups.integration.channel.type%' diff --git a/src/Marello/Bundle/UPSBundle/Resources/public/js/app/components/ups-transport-settings-component.js b/src/Marello/Bundle/UPSBundle/Resources/public/js/app/components/ups-transport-settings-component.js index 228c9f4b5..45956c70d 100644 --- a/src/Marello/Bundle/UPSBundle/Resources/public/js/app/components/ups-transport-settings-component.js +++ b/src/Marello/Bundle/UPSBundle/Resources/public/js/app/components/ups-transport-settings-component.js @@ -3,14 +3,13 @@ define(function(require) { 'use strict'; - var UPSTransportSettingsComponent; - var $ = require('jquery'); - var _ = require('underscore'); - var routing = require('routing'); - var LoadingMaskView = require('oroui/js/app/views/loading-mask-view'); - var BaseComponent = require('oroui/js/app/components/base/component'); - - UPSTransportSettingsComponent = BaseComponent.extend({ + const $ = require('jquery'); + const _ = require('underscore'); + const routing = require('routing'); + const LoadingMaskView = require('oroui/js/app/views/loading-mask-view'); + const BaseComponent = require('oroui/js/app/components/base/component'); + + const UPSTransportSettingsComponent = BaseComponent.extend({ /** * @property {Object} */ diff --git a/src/Marello/Bundle/UPSBundle/Tests/Unit/Factory/PriceRequestFactoryTest.php b/src/Marello/Bundle/UPSBundle/Tests/Unit/Factory/PriceRequestFactoryTest.php index 4550e2af7..c256060fb 100644 --- a/src/Marello/Bundle/UPSBundle/Tests/Unit/Factory/PriceRequestFactoryTest.php +++ b/src/Marello/Bundle/UPSBundle/Tests/Unit/Factory/PriceRequestFactoryTest.php @@ -13,7 +13,7 @@ use Oro\Bundle\SecurityBundle\Encoder\SymmetricCrypterInterface; use Marello\Bundle\UPSBundle\Model\Package; -use Marello\Bundle\OrderBundle\Entity\Customer; +use Marello\Bundle\CustomerBundle\Entity\Customer; use Marello\Bundle\ProductBundle\Entity\Product; use Marello\Bundle\UPSBundle\Entity\UPSSettings; use Marello\Bundle\UPSBundle\Entity\ShippingService;