custom/plugins/PickwareErpStarter/src/PickwareErpStarter.php line 57

Open in your IDE?
  1. <?php
  2. /*
  3.  * Copyright (c) Pickware GmbH. All rights reserved.
  4.  * This file is part of software that is released under a proprietary license.
  5.  * You must not copy, modify, distribute, make publicly available, or execute
  6.  * its contents or parts thereof without express permission by the copyright
  7.  * holder, unless otherwise permitted by law.
  8.  */
  9. declare(strict_types=1);
  10. namespace Pickware\PickwareErpStarter;
  11. use Doctrine\DBAL\Connection;
  12. use Pickware\ApiErrorHandlingBundle\PickwareApiErrorHandlingBundle;
  13. use Pickware\BundleInstaller\BundleInstaller;
  14. use Pickware\ConfigBundle\PickwareConfigBundle;
  15. use Pickware\DalBundle\DalBundle;
  16. use Pickware\DebugBundle\ShopwarePluginsDebugBundle;
  17. use Pickware\DocumentBundle\DocumentBundle;
  18. use Pickware\InstallationLibrary\DependencyAwareTableDropper;
  19. use Pickware\MoneyBundle\MoneyBundle;
  20. use Pickware\PickwareErpStarter\ImportExport\DependencyInjection\ExporterRegistryCompilerPass;
  21. use Pickware\PickwareErpStarter\ImportExport\DependencyInjection\ImporterRegistryCompilerPass;
  22. use Pickware\PickwareErpStarter\Installation\PickwareErpInstaller;
  23. use Pickware\PickwareErpStarter\Product\PickwareProductInitializer;
  24. use Pickware\PickwareErpStarter\Stock\Indexer\ProductReservedStockIndexer;
  25. use Pickware\PickwareErpStarter\Stock\WarehouseStockInitializer;
  26. use Pickware\PickwareErpStarter\Supplier\ProductSupplierConfigurationInitializer;
  27. use Pickware\ShopwareExtensionsBundle\PickwareShopwareExtensionsBundle;
  28. use Pickware\ShopwarePlugins\ShopwareIntegrationTestPlugin\ShopwareIntegrationTestPlugin;
  29. use Pickware\ValidationBundle\PickwareValidationBundle;
  30. use Shopware\Core\Framework\Bundle;
  31. use Shopware\Core\Framework\DataAbstractionLayer\Indexing\EntityIndexerRegistry;
  32. use Shopware\Core\Framework\Migration\MigrationCollectionLoader;
  33. use Shopware\Core\Framework\Migration\MigrationRuntime;
  34. use Shopware\Core\Framework\Migration\MigrationSource;
  35. use Shopware\Core\Framework\Parameter\AdditionalBundleParameters;
  36. use Shopware\Core\Framework\Plugin;
  37. use Shopware\Core\Framework\Plugin\Context\ActivateContext;
  38. use Shopware\Core\Framework\Plugin\Context\InstallContext;
  39. use Shopware\Core\Framework\Plugin\Context\UninstallContext;
  40. use Shopware\Core\Framework\Plugin\Context\UpdateContext;
  41. use Shopware\Core\Framework\Struct\Collection;
  42. use SwagMigrationAssistant\Migration\Writer\WriterInterface;
  43. use SwagMigrationAssistant\SwagMigrationAssistant;
  44. use Symfony\Bridge\Monolog\Logger;
  45. use Symfony\Component\Config\FileLocator;
  46. use Symfony\Component\DependencyInjection\ContainerBuilder;
  47. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  48. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  49. if (file_exists(__DIR__ '/../vendor/pickware/dependency-loader/src/DependencyLoader.php')) {
  50.     require_once __DIR__ '/../vendor/pickware/dependency-loader/src/DependencyLoader.php';
  51. }
  52. class PickwareErpStarter extends Plugin
  53. {
  54.     public const GLOBAL_PLUGIN_CONFIG_DOMAIN 'PickwareErpStarter.global-plugin-config';
  55.     public const DOCUMENT_TYPE_TECHNICAL_NAME_IMPORT 'pickware_erp_import';
  56.     public const DOCUMENT_TYPE_TECHNICAL_NAME_EXPORT 'pickware_erp_export';
  57.     public const DOCUMENT_TYPE_TECHNICAL_NAME_DESCRIPTION_MAPPING = [
  58.         self::DOCUMENT_TYPE_TECHNICAL_NAME_IMPORT => 'Imported file',
  59.         self::DOCUMENT_TYPE_TECHNICAL_NAME_EXPORT => 'Exported file',
  60.     ];
  61.     /**
  62.      * @var class-string<Bundle>[]
  63.      */
  64.     private const ADDITIONAL_BUNDLES = [
  65.         DalBundle::class,
  66.         DocumentBundle::class,
  67.         MoneyBundle::class,
  68.         PickwareApiErrorHandlingBundle::class,
  69.         PickwareShopwareExtensionsBundle::class,
  70.         PickwareValidationBundle::class,
  71.         ShopwarePluginsDebugBundle::class,
  72.         PickwareConfigBundle::class,
  73.     ];
  74.     public function getAdditionalBundles(AdditionalBundleParameters $parameters): array
  75.     {
  76.         // Ensure the bundle classes can be loaded via autoloading.
  77.         if (isset($GLOBALS['PICKWARE_DEPENDENCY_LOADER'])) {
  78.             $kernelParameters $parameters->getKernelParameters();
  79.             $GLOBALS['PICKWARE_DEPENDENCY_LOADER']->ensureLatestDependenciesOfPluginsLoaded(
  80.                 $kernelParameters['kernel.plugin_infos'],
  81.                 $kernelParameters['kernel.project_dir'],
  82.             );
  83.         }
  84.         // For some reason Collection is abstract
  85.         // phpcs:ignore Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore -- PHP CS does not understand the PHP 7 syntax
  86.         $bundleCollection = new class() extends Collection {};
  87.         foreach (self::ADDITIONAL_BUNDLES as $bundle) {
  88.             $bundle::register($bundleCollection);
  89.         }
  90.         return $bundleCollection->getElements();
  91.     }
  92.     public static function getDistPackages(): array
  93.     {
  94.         return include __DIR__ '/../Packages.php';
  95.     }
  96.     public function build(ContainerBuilder $containerBuilder): void
  97.     {
  98.         parent::build($containerBuilder);
  99.         $loader = new XmlFileLoader($containerBuilder, new FileLocator(__DIR__));
  100.         $loader->load('Address/DependencyInjection/model.xml');
  101.         $loader->load('Address/DependencyInjection/model-extension.xml');
  102.         $loader->load('Analytics/DependencyInjection/controller.xml');
  103.         $loader->load('Analytics/DependencyInjection/model.xml');
  104.         $loader->load('Analytics/DependencyInjection/model-extension.xml');
  105.         $loader->load('Analytics/DependencyInjection/service.xml');
  106.         $loader->load('BarcodeLabel/DependencyInjection/controller.xml');
  107.         $loader->load('BarcodeLabel/DependencyInjection/service.xml');
  108.         $loader->load('Cache/DependencyInjection/service.xml');
  109.         $loader->load('Config/DependencyInjection/controller.xml');
  110.         $loader->load('Config/DependencyInjection/service.xml');
  111.         $loader->load('DemandPlanning/DependencyInjection/analytics.xml');
  112.         $loader->load('DemandPlanning/DependencyInjection/controller.xml');
  113.         $loader->load('DemandPlanning/DependencyInjection/service.xml');
  114.         $loader->load('DemodataGeneration/DependencyInjection/command.xml');
  115.         $loader->load('DemodataGeneration/DependencyInjection/demodata-generator.xml');
  116.         $loader->load('ImportExport/DependencyInjection/controller.xml');
  117.         $loader->load('ImportExport/DependencyInjection/model.xml');
  118.         $loader->load('ImportExport/DependencyInjection/model-extension.xml');
  119.         $loader->load('ImportExport/DependencyInjection/model-subscriber.xml');
  120.         $loader->load('ImportExport/DependencyInjection/service.xml');
  121.         $loader->load('ImportExport/ReadWrite/DependencyInjection/service.xml');
  122.         $loader->load('Installation/DependencyInjection/service.xml');
  123.         $loader->load('InvoiceCorrection/DependencyInjection/service.xml');
  124.         $loader->load('InvoiceStack/DependencyInjection/service.xml');
  125.         $loader->load('Logger/DependencyInjection/service.xml');
  126.         $loader->load('MailDraft/DependencyInjection/controller.xml');
  127.         $loader->load('MailDraft/DependencyInjection/service.xml');
  128.         $loader->load('MessageQueueMonitoring/DependencyInjection/controller.xml');
  129.         $loader->load('MessageQueueMonitoring/DependencyInjection/service.xml');
  130.         $loader->load('Order/DependencyInjection/model.xml');
  131.         $loader->load('Order/DependencyInjection/model-extension.xml');
  132.         $loader->load('OrderCalculation/DependencyInjection/service.xml');
  133.         $loader->load('OrderPickability/DependencyInjection/decorator.xml');
  134.         $loader->load('OrderPickability/DependencyInjection/model.xml');
  135.         $loader->load('OrderPickability/DependencyInjection/model-extension.xml');
  136.         $loader->load('OrderPickability/DependencyInjection/service.xml');
  137.         $loader->load('OrderShipping/DependencyInjection/controller.xml');
  138.         $loader->load('OrderShipping/DependencyInjection/service.xml');
  139.         $loader->load('Picking/DependencyInjection/service.xml');
  140.         $loader->load('Picklist/DependencyInjection/service.xml');
  141.         $loader->load('PriceCalculation/DependencyInjection/service.xml');
  142.         $loader->load('Product/DependencyInjection/model.xml');
  143.         $loader->load('Product/DependencyInjection/model-extension.xml');
  144.         $loader->load('Product/DependencyInjection/service.xml');
  145.         $loader->load('Product/DependencyInjection/template.xml');
  146.         $loader->load('PurchaseList/DependencyInjection/controller.xml');
  147.         $loader->load('PurchaseList/DependencyInjection/exception-handler.xml');
  148.         $loader->load('PurchaseList/DependencyInjection/model.xml');
  149.         $loader->load('PurchaseList/DependencyInjection/model-extension.xml');
  150.         $loader->load('PurchaseList/DependencyInjection/service.xml');
  151.         $loader->load('Reorder/DependencyInjection/scheduled-task.xml');
  152.         $loader->load('Reorder/DependencyInjection/service.xml');
  153.         $loader->load('Reporting/DependencyInjection/model.xml');
  154.         $loader->load('Reporting/DependencyInjection/import-export.xml');
  155.         $loader->load('ReturnOrder/DependencyInjection/controller.xml');
  156.         $loader->load('ReturnOrder/DependencyInjection/model.xml');
  157.         $loader->load('ReturnOrder/DependencyInjection/service.xml');
  158.         $loader->load('Stock/DependencyInjection/container-override.xml');
  159.         $loader->load('Stock/DependencyInjection/decorator.xml');
  160.         $loader->load('Stock/DependencyInjection/exception-handler.xml');
  161.         $loader->load('Stock/DependencyInjection/import-export.xml');
  162.         $loader->load('Stock/DependencyInjection/indexer.xml');
  163.         $loader->load('Stock/DependencyInjection/model.xml');
  164.         $loader->load('Stock/DependencyInjection/model-extension.xml');
  165.         $loader->load('Stock/DependencyInjection/product-sales-update.xml');
  166.         $loader->load('Stock/DependencyInjection/service.xml');
  167.         $loader->load('StockApi/DependencyInjection/controller.xml');
  168.         $loader->load('StockApi/DependencyInjection/service.xml');
  169.         $loader->load('StockApi/DependencyInjection/subscriber.xml');
  170.         $loader->load('StockFlow/DependencyInjection/controller.xml');
  171.         $loader->load('StockFlow/DependencyInjection/service.xml');
  172.         $loader->load('Stocking/DependencyInjection/service.xml');
  173.         $loader->load('GoodsReceipt/DependencyInjection/controller.xml');
  174.         $loader->load('GoodsReceipt/DependencyInjection/model.xml');
  175.         $loader->load('GoodsReceipt/DependencyInjection/service.xml');
  176.         $loader->load('Stocktaking/DependencyInjection/controller.xml');
  177.         $loader->load('Stocktaking/DependencyInjection/exception-handler.xml');
  178.         $loader->load('Stocktaking/DependencyInjection/import-export.xml');
  179.         $loader->load('Stocktaking/DependencyInjection/model.xml');
  180.         $loader->load('Stocktaking/DependencyInjection/model-extension.xml');
  181.         $loader->load('Stocktaking/DependencyInjection/service.xml');
  182.         $loader->load('Stocktaking/ProductSummary/DependencyInjection/indexer.xml');
  183.         $loader->load('Stocktaking/ProductSummary/DependencyInjection/model.xml');
  184.         $loader->load('Stocktaking/ProductSummary/DependencyInjection/model-extension.xml');
  185.         $loader->load('Stocktaking/ProductSummary/DependencyInjection/service.xml');
  186.         $loader->load('Supplier/DependencyInjection/exception-handler.xml');
  187.         $loader->load('Supplier/DependencyInjection/import-export.xml');
  188.         $loader->load('Supplier/DependencyInjection/model.xml');
  189.         $loader->load('Supplier/DependencyInjection/model-extension.xml');
  190.         $loader->load('Supplier/DependencyInjection/model-subscriber.xml');
  191.         $loader->load('Supplier/DependencyInjection/service.xml');
  192.         $loader->load('Supplier/DependencyInjection/indexer.xml');
  193.         $loader->load('SupplierOrder/DependencyInjection/controller.xml');
  194.         $loader->load('SupplierOrder/DependencyInjection/import-export.xml');
  195.         $loader->load('SupplierOrder/DependencyInjection/indexer.xml');
  196.         $loader->load('SupplierOrder/DependencyInjection/model.xml');
  197.         $loader->load('SupplierOrder/DependencyInjection/service.xml');
  198.         $loader->load('Translation/DependencyInjection/service.xml');
  199.         $loader->load('Warehouse/DependencyInjection/exception-handler.xml');
  200.         $loader->load('Warehouse/DependencyInjection/import-export.xml');
  201.         $loader->load('Warehouse/DependencyInjection/model.xml');
  202.         $loader->load('Warehouse/DependencyInjection/model-extension.xml');
  203.         $loader->load('Warehouse/DependencyInjection/service.xml');
  204.         $loader->load('Warehouse/DependencyInjection/subscriber.xml');
  205.         // Register test services. Should never be loaded in production.
  206.         if (in_array(ShopwareIntegrationTestPlugin::class, $containerBuilder->getParameter('kernel.bundles'), true)) {
  207.             $loader->load('../test/TestEntityCreation/DependencyInjection/service.xml');
  208.             $loader->load('Benchmarking/DependencyInjection/benchmark.xml');
  209.         }
  210.         $containerBuilder->addCompilerPass(new ImporterRegistryCompilerPass());
  211.         $containerBuilder->addCompilerPass(new ExporterRegistryCompilerPass());
  212.         // Add SwagMigrationAssistant service decoration when the plugin is present.
  213.         $activePlugins $containerBuilder->getParameter('kernel.active_plugins');
  214.         if (isset($activePlugins[SwagMigrationAssistant::class]) && interface_exists(WriterInterface::class)) {
  215.             $loader->load('ShopwareMigration/DependencyInjection/service.xml');
  216.         }
  217.     }
  218.     public function install(InstallContext $installContext): void
  219.     {
  220.         $this->loadDependenciesForSetup();
  221.         $this->executeMigrationsOfBundles();
  222.         BundleInstaller::createForContainerAndClass($this->containerself::class)
  223.             ->install(self::ADDITIONAL_BUNDLES$installContext);
  224.     }
  225.     public function postInstall(InstallContext $installContext): void
  226.     {
  227.         $installer PickwareErpInstaller::initFromContainer($this->container);
  228.         $installer->postInstall($installContext);
  229.     }
  230.     public function update(UpdateContext $updateContext): void
  231.     {
  232.         $this->loadDependenciesForSetup();
  233.         $this->executeMigrationsOfBundles();
  234.         BundleInstaller::createForContainerAndClass($this->containerself::class)
  235.             ->install(self::ADDITIONAL_BUNDLES$updateContext);
  236.     }
  237.     public function postUpdate(UpdateContext $updateContext): void
  238.     {
  239.         $installer PickwareErpInstaller::initFromContainer($this->container);
  240.         $installer->postUpdate($updateContext);
  241.         if ($updateContext->getPlugin()->isActive()) {
  242.             BundleInstaller::createForContainerAndClass($this->containerself::class)
  243.                 ->onAfterActivate(self::ADDITIONAL_BUNDLES$updateContext);
  244.         }
  245.     }
  246.     private function executeMigrationsOfBundles(): void
  247.     {
  248.         // All the services required for migration execution are private in the DI-Container. As a workaround the
  249.         // services are instantiated explicitly here.
  250.         /** @var Connection $db */
  251.         $db $this->container->get(Connection::class);
  252.         // See vendor/symfony/monolog-bundle/Resources/config/monolog.xml on how the logger is defined.
  253.         $logger = new Logger('app');
  254.         $logger->useMicrosecondTimestamps($this->container->getParameter('monolog.use_microseconds'));
  255.         $migrationCollectionLoader = new MigrationCollectionLoader($db, new MigrationRuntime($db$logger));
  256.         $migrationSource = new MigrationSource('PickwareErpStarter');
  257.         foreach (self::ADDITIONAL_BUNDLES as $bundle) {
  258.             $bundle::registerMigrations($migrationSource);
  259.         }
  260.         $migrationCollectionLoader->addSource($migrationSource);
  261.         foreach ($migrationCollectionLoader->collectAll() as $migrationCollection) {
  262.             $migrationCollection->sync();
  263.             $migrationCollection->migrateInPlace();
  264.         }
  265.     }
  266.     public function activate(ActivateContext $activateContext): void
  267.     {
  268.         /** @var Connection $db */
  269.         $db $this->container->get(Connection::class);
  270.         /** @var EventDispatcherInterface $eventDispatcher */
  271.         $eventDispatcher $this->container->get('event_dispatcher');
  272.         /** @var EntityIndexerRegistry $entityIndexerRegistry */
  273.         $entityIndexerRegistry $this->container->get('pickware.pickware_erp.entity_indexer_registry_public');
  274.         // Subscribers take care of calculated values (e.g. stocks) and mandatory entity extensions (e.g. pickware
  275.         // product) during the runtime of the shop while the plugin is installed.
  276.         // Indexers repair the system when (for whatever reason) the subscribers fail or the information base changed
  277.         // unknowingly. They should only be called manually.
  278.         // To ensure that the system is in the correct state after the plugin is (1) installed for the first time or
  279.         // (2) activated/reinstalled after it was deactivated/uninstalled for a period of time, we need to redo the
  280.         // subscriber's job. But we _only upsert_ the mandatory entity extensions and _do not recalculate_ values for
  281.         // performance reasons. Recalculating values in every update eventually overloads the message queue.
  282.         (new WarehouseStockInitializer($db))->ensureProductWarehouseStocksExistsForAllProducts();
  283.         (new PickwareProductInitializer($db$eventDispatcher))->ensurePickwareProductsExistForAllProducts();
  284.         (new ProductSupplierConfigurationInitializer($db))->ensureProductSupplierConfigurationExistsForAllProducts();
  285.         // When deactivating/uninstalling this plugin for a period of time while the shops is active, we need to
  286.         // recalculate certain non-erp based indexer: reserved stock (based on orders). This is especially true when
  287.         // installing the plugin for the first time.
  288.         // We _do not_ recalculate stock as no stock movements are written while this plugin is deactivated/uninstalled
  289.         // (or was never installed in the first place).
  290.         $entityIndexerRegistry->sendIndexingMessage([
  291.             ProductReservedStockIndexer::NAME,
  292.         ]);
  293.         BundleInstaller::createForContainerAndClass($this->containerself::class)
  294.             ->onAfterActivate(self::ADDITIONAL_BUNDLES$activateContext);
  295.     }
  296.     public function uninstall(UninstallContext $uninstallContext): void
  297.     {
  298.         parent::uninstall($uninstallContext);
  299.         if ($uninstallContext->keepUserData()) {
  300.             return;
  301.         }
  302.         $this->loadDependenciesForSetup();
  303.         $this->container->get(Connection::class)->executeStatement('
  304.             -- Migration1589893337AddWarehouseCustomFields.php
  305.             DELETE FROM `custom_field_set_relation` WHERE `entity_name` = "pickware_erp_warehouse";
  306.             -- Migration1599487702CreateProductReorderView.php
  307.             DROP VIEW IF EXISTS `pickware_erp_product_reorder_view`;
  308.             -- Migration1605002744CreateOrderPickabilityView.php
  309.             DROP VIEW IF EXISTS `pickware_erp_order_pickability_view`;
  310.             -- Migration1606220870CreateStockValuationView.php
  311.             DROP VIEW IF EXISTS `pickware_erp_stock_valuation_view`;
  312.         ');
  313.         DependencyAwareTableDropper::createForContainer($this->container)->dropTables([
  314.             'pickware_erp_address',
  315.             'pickware_erp_warehouse',
  316.             'pickware_erp_bin_location',
  317.             'pickware_erp_special_stock_location',
  318.             'pickware_erp_location_type',
  319.             'pickware_erp_stock_movement',
  320.             'pickware_erp_stock',
  321.             'pickware_erp_config',
  322.             'pickware_erp_product_warehouse_configuration',
  323.             'pickware_erp_product_configuration',
  324.             'pickware_erp_pickware_product',
  325.             'pickware_erp_warehouse_stock',
  326.             'pickware_erp_supplier',
  327.             'pickware_erp_product_supplier_configuration',
  328.             'pickware_erp_import_export',
  329.             'pickware_erp_import_element',
  330.             'pickware_erp_import_export_profile',
  331.             'pickware_erp_message_queue_monitoring',
  332.             'pickware_erp_demand_planning_session',
  333.             'pickware_erp_demand_planning_list_item',
  334.             'pickware_erp_purchase_list_item',
  335.             'pickware_erp_import_export_element',
  336.             'pickware_erp_supplier_order',
  337.             'pickware_erp_supplier_order_line_item',
  338.             'pickware_erp_analytics_profile',
  339.             'pickware_erp_analytics_session',
  340.             'pickware_erp_analytics_list_item_demand_planning',
  341.             'pickware_erp_stock_container',
  342.             'pickware_erp_return_order',
  343.             'pickware_erp_return_order_refund',
  344.             'pickware_erp_return_order_line_item',
  345.             'pickware_erp_return_order_document_mapping',
  346.             'pickware_erp_analytics_aggregation',
  347.             'pickware_erp_analytics_aggregation_session',
  348.             'pickware_erp_analytics_report',
  349.             'pickware_erp_analytics_report_config',
  350.             'pickware_erp_analytics_aggregation_item_demand_planning',
  351.             'pickware_erp_demand_planning_list_item',
  352.             'pickware_erp_order_pickability',
  353.             'pickware_erp_stocktaking_stocktake',
  354.             'pickware_erp_stocktaking_stocktake_counting_process',
  355.             'pickware_erp_stocktaking_stocktake_counting_process_item',
  356.             'pickware_erp_stocktaking_stocktake_product_summary',
  357.             'pickware_erp_stocktaking_stocktake_snapshot_item',
  358.             'pickware_erp_product_sales_update_queue',
  359.             'pickware_erp_goods_receipt',
  360.             'pickware_erp_goods_receipt_item',
  361.         ]);
  362.         PickwareErpInstaller::initFromContainer($this->container)->uninstall($uninstallContext);
  363.         BundleInstaller::createForContainerAndClass($this->containerself::class)->uninstall($uninstallContext);
  364.     }
  365.     /**
  366.      * Run the dependency loader for a setup step like install/update/uninstall
  367.      *
  368.      * When executing one of these steps but no Pickware plugin is activated, the dependency loader did never run until
  369.      * the call of the corresponding method. You can trigger it with a call of this method.
  370.      */
  371.     private function loadDependenciesForSetup(): void
  372.     {
  373.         if (isset($GLOBALS['PICKWARE_DEPENDENCY_LOADER'])) {
  374.             $plugins $this->container->get('kernel')->getPluginLoader()->getPluginInfos();
  375.             $projectDir $this->container->getParameter('kernel.project_dir');
  376.             $GLOBALS['PICKWARE_DEPENDENCY_LOADER']->ensureLatestDependenciesOfPluginsLoaded($plugins$projectDir);
  377.         }
  378.     }
  379. }