custom/plugins/CogiAffiliate/src/CogiAffiliate.php line 27

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Cogi\Affiliate;
  3. use Cogi\Affiliate\ScheduledTask\TransactionTask;
  4. use Doctrine\DBAL\Connection;
  5. use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
  6. use Shopware\Core\Content\ImportExport\Exception\FileNotFoundException;
  7. use Shopware\Core\Content\MailTemplate\Aggregate\MailTemplateType\MailTemplateTypeEntity;
  8. use Shopware\Core\Defaults;
  9. use Shopware\Core\Framework\Context;
  10. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  11. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  14. use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskDefinition;
  15. use Shopware\Core\Framework\Plugin;
  16. use Shopware\Core\Framework\Plugin\Context\InstallContext;
  17. use Shopware\Core\Framework\Plugin\Context\UninstallContext;
  18. use Shopware\Core\Framework\Plugin\Context\UpdateContext;
  19. use Shopware\Core\Framework\Uuid\Uuid;
  20. use Shopware\Core\System\CustomField\CustomFieldTypes;
  21. use Shopware\Core\System\Language\LanguageEntity;
  22. use Shopware\Core\System\NumberRange\Aggregate\NumberRangeType\NumberRangeTypeEntity;
  23. use Shopware\Core\System\NumberRange\NumberRangeEntity;
  24. class CogiAffiliate extends Plugin
  25. {
  26.     // Settings
  27.     public const MIN_PAYOUT 100// Minimum balance for payout
  28.     public const DAYS_TO_WAIT 15// At least 14 days correspond to the withdrawal period
  29.     // Mails
  30.     public const ADMIN_NEW_APPLICATION_TEMPLATE_TYPE_TECHNICAL_NAME 'ca_admin_new_application';
  31.     public const AFFILIATE_APPLICATION_ACCEPT_TEMPLATE_TYPE_TECHNICAL_NAME 'ca_affiliate_application_accept';
  32.     public const ADMIN_PAYOUT_REQUEST_TEMPLATE_TYPE_TECHNICAL_NAME 'ca_admin_payout_request';
  33.     public const ADMIN_NEW_TRANSACTION_TEMPLATE_TYPE_TECHNICAL_NAME 'ca_admin_new_transaction';
  34.     public const AFFILIATE_NEW_TRANSACTION_TEMPLATE_TYPE_TECHNICAL_NAME 'ca_affiliate_new_transaction';
  35.     public const AFFILIATE_PAYOUT_DOCUMENT_TEMPLATE_TYPE_TECHNICAL_NAME 'ca_affiliate_payout_document';
  36.     public const AFFILIATE_SEPA_XML_TEMPLATE_TYPE_TECHNICAL_NAME 'ca_affiliate_sepa_xml';
  37.     // Number range
  38.     public const AFFILIATE_PAYOUT_NUMBER_RANGE 'cogi_affiliate_payout';
  39.     /**
  40.      * Installs the plugin (Email templates, custom field set)
  41.      * @param InstallContext $installContext
  42.      * @return void
  43.      */
  44.     public function install(InstallContext $installContext): void
  45.     {
  46.         // Install custom field set
  47.         $this->installCustomFieldSet($installContext->getContext());
  48.         // Install email templates
  49.         $this->installEmailTemplates($installContext->getContext());
  50.     }
  51.     /**
  52.      * postUpdate installs scheduled task, email templates and custom field set, if not existent
  53.      * @param UpdateContext $updateContext
  54.      * @return void
  55.      */
  56.     public function postUpdate(UpdateContext $updateContext): void
  57.     {
  58.         parent::postUpdate($updateContext);
  59.         // Install scheduled task, if not existent (already exists for newer versions)
  60.         try {
  61.             $scheduledTaskRepository $this->container->get('scheduled_task.repository');
  62.             $task = new TransactionTask();
  63.             $scheduledTaskRepository->create([
  64.                 [
  65.                     'name' => $task::getTaskName(),
  66.                     'scheduledTaskClass' => get_class($task),
  67.                     'runInterval' => $task::getDefaultInterval(),
  68.                     'status' => ScheduledTaskDefinition::STATUS_SCHEDULED,
  69.                 ],
  70.             ], Context::createDefaultContext());
  71.         } catch (\Exception $e) {}
  72.         // Update custom field set
  73.         $this->removeCustomFieldSet($updateContext->getContext());
  74.         $this->installCustomFieldSet($updateContext->getContext());
  75.         // Update email templates
  76.         $this->installEmailTemplates($updateContext->getContext());
  77.     }
  78.     /**
  79.      * Uninstalls the extension
  80.      * @param UninstallContext $uninstallContext
  81.      * @return void
  82.      */
  83.     public function uninstall(UninstallContext $uninstallContext): void
  84.     {
  85.         parent::uninstall($uninstallContext);
  86.         if ($uninstallContext->keepUserData()) {
  87.             return;
  88.         }
  89.         $connection $this->container->get(Connection::class);
  90.         $connection->executeStatement('
  91.             DROP TABLE IF EXISTS `ca_product_group_product`;
  92.             DROP TABLE IF EXISTS `ca_product_group_affiliate`;
  93.             DROP TABLE IF EXISTS `ca_product_group`;
  94.             DROP TABLE IF EXISTS `ca_partner_group`;
  95.             DROP TABLE IF EXISTS `ca_transaction`;
  96.             DROP TABLE IF EXISTS `ca_promotion_customer`;
  97.             ');
  98.         try {
  99.             $connection->executeStatement('
  100.             ALTER TABLE `product`
  101.             DROP COLUMN `productGroups`;
  102.             ');
  103.         } catch (\Exception $e) {
  104.         }
  105.         try {
  106.             $connection->executeStatement('
  107.             ALTER TABLE `promotion`
  108.             DROP COLUMN `affiliatePartner`;
  109.             ');
  110.         } catch (\Exception $e) {
  111.         }
  112.         try {
  113.             $connection->executeStatement('
  114.             ALTER TABLE `customer`
  115.             DROP COLUMN `affiliatePromotion`;
  116.             ');
  117.         } catch (\Exception $e) {
  118.         }
  119.         try {
  120.             $connection->executeStatement('
  121.             ALTER TABLE `product`
  122.             DROP COLUMN `ca_product_group_id`;
  123.             ');
  124.         } catch (\Exception $e) {
  125.         }
  126.         // Uninstall custom field set
  127.         $this->removeCustomFieldSet($uninstallContext->getContext());
  128.         // Uninstall email templates
  129.         $this->removeEmailTemplate(self::ADMIN_NEW_APPLICATION_TEMPLATE_TYPE_TECHNICAL_NAME$uninstallContext->getContext());
  130.         $this->removeEmailTemplate(self::AFFILIATE_APPLICATION_ACCEPT_TEMPLATE_TYPE_TECHNICAL_NAME$uninstallContext->getContext());
  131.         $this->removeEmailTemplate(self::ADMIN_PAYOUT_REQUEST_TEMPLATE_TYPE_TECHNICAL_NAME$uninstallContext->getContext());
  132.         $this->removeEmailTemplate(self::ADMIN_NEW_TRANSACTION_TEMPLATE_TYPE_TECHNICAL_NAME$uninstallContext->getContext());
  133.         $this->removeEmailTemplate(self::AFFILIATE_NEW_TRANSACTION_TEMPLATE_TYPE_TECHNICAL_NAME$uninstallContext->getContext());
  134.         $this->removeEmailTemplate(self::AFFILIATE_PAYOUT_DOCUMENT_TEMPLATE_TYPE_TECHNICAL_NAME$uninstallContext->getContext());
  135.         $this->removeEmailTemplate(self::AFFILIATE_SEPA_XML_TEMPLATE_TYPE_TECHNICAL_NAME$uninstallContext->getContext());
  136.         // Uninstall number range
  137.         $this->removeNumberRange($connection$uninstallContext->getContext());
  138.     }
  139.     /**
  140.      * Installs the custom field set
  141.      * @param Context $context
  142.      * @return void
  143.      */
  144.     public function installCustomFieldSet(Context $context) {
  145.         $customFieldSetRepository $this->container->get('custom_field_set.repository');
  146.         $fieldSetId $this->getFieldSetId($customFieldSetRepository$context);
  147.         if (!$fieldSetId) {
  148.             $customFieldSetRepository->create([[
  149.                 'name' => 'cogi_affiliate_set',
  150.                 'customFields' => [
  151.                     [
  152.                         'name' => 'ca_active',
  153.                         'type' => CustomFieldTypes::BOOL,
  154.                         'config' => [
  155.                             'type' => CustomFieldTypes::SWITCH,
  156.                             'label' => [
  157.                                 'en-GB' => 'Active',
  158.                                 'de-DE' => 'Aktiv'
  159.                             ],
  160.                             'helpText' => [
  161.                                 'en-GB' => 'Indicates whether an affiliate is active. Can be changed in the affiliate overview.',
  162.                                 'de-DE' => 'Gibt an, ob ein Affiliate aktiv ist. Kann in der Affiliate-Übersicht geändert werden.'
  163.                             ],
  164.                             'componentName' => 'sw-field',
  165.                             'customFieldType' => CustomFieldTypes::SWITCH,
  166.                             'customFieldPosition' => 1,
  167.                             'translated' => false,
  168.                             'disabled' => true
  169.                         ],
  170.                     ], [
  171.                         'name' => 'ca_lifetime_affiliate_id',
  172.                         'type' => CustomFieldTypes::ENTITY,
  173.                         'config' => [
  174.                             'entity' => 'customer',
  175.                             'label' => [
  176.                                 'en-GB' => 'Assign lifetime affiliate',
  177.                                 'de-DE' => 'Dauerhaften Affiliate zuweisen'
  178.                             ],
  179.                             'placeholder' => [
  180.                                 'en-GB' => 'Lifetime affiliate',
  181.                                 'de-DE' => 'Dauerhafter Affiliate'
  182.                             ],
  183.                             'helpText' => [
  184.                                 'en-GB' => 'Provisions every valid order in favor of the affiliate, if activated in the plugin configuration',
  185.                                 'de-DE' => 'Provisioniert jede gültige Bestellung zugunsten des Affiliates, sofern in der Plugin-Konfiguration aktiviert'
  186.                             ],
  187.                             'componentName' => 'sw-entity-single-select',
  188.                             'customFieldType' => CustomFieldTypes::ENTITY,
  189.                             'customFieldPosition' => 2,
  190.                             'labelProperty' => ["firstName""lastName"],
  191.                             'translated' => false
  192.                         ],
  193.                     ], [
  194.                         'name' => 'ca_code',
  195.                         'type' => CustomFieldTypes::TEXT,
  196.                         'config' => [
  197.                             'label' => [
  198.                                 'en-GB' => 'Affiliate code',
  199.                                 'de-DE' => 'Affiliate-Code'
  200.                             ],
  201.                             'placeholder' => [
  202.                                 'en-GB' => 'Affiliate code',
  203.                                 'de-DE' => 'Affiliate-Code'
  204.                             ],
  205.                             'helpText' => [
  206.                                 'en-GB' => 'Can be changed in the affiliate overview.',
  207.                                 'de-DE' => 'Kann in der Affiliate-Übersicht geändert werden.'
  208.                             ],
  209.                             'customFieldType' => CustomFieldTypes::TEXT,
  210.                             'customFieldPosition' => 3,
  211.                             'translated' => false,
  212.                             'disabled' => true
  213.                         ],
  214.                     ], [
  215.                         'name' => 'ca_multiplier',
  216.                         'type' => 'number',
  217.                         'config' => [
  218.                             'label' => [
  219.                                 'en-GB' => 'Individual provision multiplier',
  220.                                 'de-DE' => 'Individueller Provisionsmultiplikator'
  221.                             ],
  222.                             'placeholder' => [
  223.                                 'en-GB' => 'Individual provision multiplier',
  224.                                 'de-DE' => 'Individueller Provisionsmultiplikator'
  225.                             ],
  226.                             'helpText' => [
  227.                                 'en-GB' => 'Can be changed in the affiliate overview.',
  228.                                 'de-DE' => 'Kann in der Affiliate-Übersicht geändert werden.'
  229.                             ],
  230.                             'numberType' => CustomFieldTypes::FLOAT,
  231.                             'componentName' => 'sw-field',
  232.                             'customFieldType' => 'number',
  233.                             'max' => 10,
  234.                             'min' => 0,
  235.                             'step' => 0.01,
  236.                             'customFieldPosition' => 4,
  237.                             'translated' => false,
  238.                             'disabled' => true
  239.                         ],
  240.                     ], [
  241.                         'name' => 'ca_iban',
  242.                         'type' => CustomFieldTypes::TEXT,
  243.                         'config' => [
  244.                             'label' => [
  245.                                 'en-GB' => 'IBAN',
  246.                                 'de-DE' => 'IBAN'
  247.                             ],
  248.                             'placeholder' => [
  249.                                 'en-GB' => 'IBAN',
  250.                                 'de-DE' => 'IBAN'
  251.                             ],
  252.                             'helpText' => [
  253.                                 'en-GB' => 'Can be changed in the affiliate overview.',
  254.                                 'de-DE' => 'Kann in der Affiliate-Übersicht geändert werden.'
  255.                             ],
  256.                             'customFieldType' => CustomFieldTypes::TEXT,
  257.                             'customFieldPosition' => 5,
  258.                             'translated' => false,
  259.                             'disabled' => true
  260.                         ],
  261.                     ], [
  262.                         'name' => 'ca_bic',
  263.                         'type' => CustomFieldTypes::TEXT,
  264.                         'config' => [
  265.                             'label' => [
  266.                                 'en-GB' => 'BIC',
  267.                                 'de-DE' => 'BIC'
  268.                             ],
  269.                             'placeholder' => [
  270.                                 'en-GB' => 'BIC',
  271.                                 'de-DE' => 'BIC'
  272.                             ],
  273.                             'helpText' => [
  274.                                 'en-GB' => 'Can be changed in the affiliate overview.',
  275.                                 'de-DE' => 'Kann in der Affiliate-Übersicht geändert werden.'
  276.                             ],
  277.                             'customFieldType' => CustomFieldTypes::TEXT,
  278.                             'customFieldPosition' => 6,
  279.                             'translated' => false,
  280.                             'disabled' => true
  281.                         ],
  282.                     ], [
  283.                         'name' => 'ca_company',
  284.                         'type' => CustomFieldTypes::TEXT,
  285.                         'config' => [
  286.                             'label' => [
  287.                                 'en-GB' => 'Company',
  288.                                 'de-DE' => 'Firma'
  289.                             ],
  290.                             'placeholder' => [
  291.                                 'en-GB' => 'Company',
  292.                                 'de-DE' => 'Firma'
  293.                             ],
  294.                             'helpText' => [
  295.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  296.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  297.                             ],
  298.                             'customFieldType' => CustomFieldTypes::TEXT,
  299.                             'customFieldPosition' => 7,
  300.                             'translated' => false
  301.                         ],
  302.                     ], [
  303.                         'name' => 'ca_name',
  304.                         'type' => CustomFieldTypes::TEXT,
  305.                         'config' => [
  306.                             'label' => [
  307.                                 'en-GB' => 'Name',
  308.                                 'de-DE' => 'Name'
  309.                             ],
  310.                             'placeholder' => [
  311.                                 'en-GB' => 'Name',
  312.                                 'de-DE' => 'Name'
  313.                             ],
  314.                             'helpText' => [
  315.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  316.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  317.                             ],
  318.                             'customFieldType' => CustomFieldTypes::TEXT,
  319.                             'customFieldPosition' => 8,
  320.                             'translated' => false
  321.                         ],
  322.                     ], [
  323.                         'name' => 'ca_street',
  324.                         'type' => CustomFieldTypes::TEXT,
  325.                         'config' => [
  326.                             'label' => [
  327.                                 'en-GB' => 'Street address',
  328.                                 'de-DE' => 'Straße und Hausnummer'
  329.                             ],
  330.                             'placeholder' => [
  331.                                 'en-GB' => 'Street address',
  332.                                 'de-DE' => 'Straße und Hausnummer'
  333.                             ],
  334.                             'helpText' => [
  335.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  336.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  337.                             ],
  338.                             'customFieldType' => CustomFieldTypes::TEXT,
  339.                             'customFieldPosition' => 9,
  340.                             'translated' => false
  341.                         ],
  342.                     ], [
  343.                         'name' => 'ca_zipcode',
  344.                         'type' => CustomFieldTypes::TEXT,
  345.                         'config' => [
  346.                             'label' => [
  347.                                 'en-GB' => 'Postal code',
  348.                                 'de-DE' => 'PLZ'
  349.                             ],
  350.                             'placeholder' => [
  351.                                 'en-GB' => 'Postal code',
  352.                                 'de-DE' => 'PLZ'
  353.                             ],
  354.                             'helpText' => [
  355.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  356.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  357.                             ],
  358.                             'customFieldType' => CustomFieldTypes::TEXT,
  359.                             'customFieldPosition' => 10,
  360.                             'translated' => false
  361.                         ],
  362.                     ], [
  363.                         'name' => 'ca_city',
  364.                         'type' => CustomFieldTypes::TEXT,
  365.                         'config' => [
  366.                             'label' => [
  367.                                 'en-GB' => 'City',
  368.                                 'de-DE' => 'Ort'
  369.                             ],
  370.                             'placeholder' => [
  371.                                 'en-GB' => 'City',
  372.                                 'de-DE' => 'Ort'
  373.                             ],
  374.                             'helpText' => [
  375.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  376.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  377.                             ],
  378.                             'customFieldType' => CustomFieldTypes::TEXT,
  379.                             'customFieldPosition' => 11,
  380.                             'translated' => false
  381.                         ],
  382.                     ], [
  383.                         'name' => 'ca_vatid',
  384.                         'type' => CustomFieldTypes::TEXT,
  385.                         'config' => [
  386.                             'label' => [
  387.                                 'en-GB' => 'VAT Reg.No.',
  388.                                 'de-DE' => 'Umsatzsteuer-ID'
  389.                             ],
  390.                             'placeholder' => [
  391.                                 'en-GB' => 'VAT Reg.No.',
  392.                                 'de-DE' => 'Umsatzsteuer-ID'
  393.                             ],
  394.                             'helpText' => [
  395.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  396.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  397.                             ],
  398.                             'customFieldType' => CustomFieldTypes::TEXT,
  399.                             'customFieldPosition' => 12,
  400.                             'translated' => false
  401.                         ],
  402.                     ], [
  403.                         'name' => 'ca_vatexempt',
  404.                         'type' => CustomFieldTypes::BOOL,
  405.                         'config' => [
  406.                             'type' => CustomFieldTypes::SWITCH,
  407.                             'label' => [
  408.                                 'en-GB' => 'VAT exempt',
  409.                                 'de-DE' => 'Umsatzsteuer befreit'
  410.                             ],
  411.                             'helpText' => [
  412.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  413.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  414.                             ],
  415.                             'componentName' => 'sw-field',
  416.                             'customFieldType' => CustomFieldTypes::SWITCH,
  417.                             'customFieldPosition' => 13,
  418.                             'translated' => false
  419.                         ],
  420.                     ], [
  421.                         'name' => 'ca_taxnumber',
  422.                         'type' => CustomFieldTypes::TEXT,
  423.                         'config' => [
  424.                             'label' => [
  425.                                 'en-GB' => 'Tax no',
  426.                                 'de-DE' => 'Steuernummer'
  427.                             ],
  428.                             'placeholder' => [
  429.                                 'en-GB' => 'Tax no',
  430.                                 'de-DE' => 'Steuernummer'
  431.                             ],
  432.                             'helpText' => [
  433.                                 'en-GB' => 'Used only if the configuration of the extended master data is activated',
  434.                                 'de-DE' => 'Wird nur verwendet, wenn die Konfiguration der erweiterten Stammdaten aktiviert ist'
  435.                             ],
  436.                             'customFieldType' => CustomFieldTypes::TEXT,
  437.                             'customFieldPosition' => 14,
  438.                             'translated' => false
  439.                         ],
  440.                     ]
  441.                 ],
  442.                 'relations' => [
  443.                     [
  444.                         'entityName' => 'customer'
  445.                     ]
  446.                 ],
  447.                 'config' => [
  448.                     'label' => [
  449.                         'en-GB' => 'Affiliate Marketing',
  450.                         'de-DE' => 'Affiliate-Marketing'
  451.                     ],
  452.                     'translated' => true
  453.                 ],
  454.             ]], $context);
  455.         }
  456.     }
  457.     /**
  458.      * Removes the custom field set
  459.      * @param Context $context
  460.      * @return void
  461.      */
  462.     public function removeCustomFieldSet(Context $context) {
  463.         $customFieldSetRepository $this->container->get('custom_field_set.repository');
  464.         $fieldSetId $this->getFieldSetId($customFieldSetRepository$context);
  465.         if ($fieldSetId) {
  466.             $customFieldSetRepository->delete([["id" => $fieldSetId]], $context);
  467.         }
  468.     }
  469.     /**
  470.      * Gets the custom field set id
  471.      * @param $customFieldSetRepository
  472.      * @param Context $context
  473.      * @return string|null
  474.      */
  475.     public function getFieldSetId($customFieldSetRepositoryContext $context): ?string
  476.     {
  477.         $criteria = new Criteria();
  478.         $criteria->addFilter(new EqualsFilter('custom_field_set.name''cogi_affiliate_set'));
  479.         $customFieldSet $customFieldSetRepository->search($criteria$context)->first();
  480.         return (!$customFieldSet null $customFieldSet->getId());
  481.     }
  482.     /**
  483.      * Installs all email templates
  484.      * @param Context $context
  485.      * @return void
  486.      */
  487.     public function installEmailTemplates(Context $context) {
  488.         // To store owner: Application mail
  489.         $this->addEmailTemplate(
  490.             [
  491.                 'en-GB' => 'Affiliate Marketing (Admin): Affiliate application',
  492.                 'de-DE' => 'Affiliate-Marketing (Admin): Affiliate-Bewerbung'
  493.             ],
  494.             self::ADMIN_NEW_APPLICATION_TEMPLATE_TYPE_TECHNICAL_NAME,
  495.             [
  496.                 'en-GB' => 'A new affiliate has registered!',
  497.                 'de-DE' => 'Ein neuer Affiliate hat sich beworben!'
  498.             ],
  499.             [
  500.                 'en-GB' => 'This template is sent to the store owner as soon as a new affiliate applies.',
  501.                 'de-DE' => 'Dieses Template wird an den Shopbetreiber verschickt, sobald sich ein neuer Affiliate bewirbt.'
  502.             ],
  503.             [
  504.                 'customer' => 'customer',
  505.                 'salesChannel' => 'sales_channel'
  506.             ], $context);
  507.         // To affiliate: Application accepted
  508.         $this->addEmailTemplate(
  509.             [
  510.                 'en-GB' => 'Affiliate Marketing (Affiliate): Application accepted',
  511.                 'de-DE' => 'Affiliate-Marketing (Affiliate): Bewerbung akzeptiert'
  512.             ],
  513.             self::AFFILIATE_APPLICATION_ACCEPT_TEMPLATE_TYPE_TECHNICAL_NAME,
  514.             [
  515.                 'en-GB' => 'Your affiliate-application has been accepted!',
  516.                 'de-DE' => 'Ihre Affiliate-Bewerbung wurde akzeptiert!'
  517.             ],
  518.             [
  519.                 'en-GB' => 'This template is sent to the affiliate as soon as his application is accepted.',
  520.                 'de-DE' => 'Dieses Template wird an den Affiliate verschickt, sobald seine Bewerbung akzeptiert wurde.'
  521.             ],
  522.             [
  523.                 'customer' => 'customer',
  524.                 'salesChannel' => 'sales_channel'
  525.             ], $context);
  526.         // To store owner: Payout request mail
  527.         $this->addEmailTemplate(
  528.             [
  529.                 'en-GB' => 'Affiliate Marketing (Admin): Affiliate payout request',
  530.                 'de-DE' => 'Affiliate-Marketing (Admin): Auszahlungsanfrage von Affiliate'
  531.             ],
  532.             self::ADMIN_PAYOUT_REQUEST_TEMPLATE_TYPE_TECHNICAL_NAME,
  533.             [
  534.                 'en-GB' => 'New payout request from an affiliate',
  535.                 'de-DE' => 'Neue Auszahlungsanfrage eines Affiliates'
  536.             ],
  537.             [
  538.                 'en-GB' => 'This template is sent to the store owner for each payout request by an affiliate.',
  539.                 'de-DE' => 'Dieses Template wird an den Shopbetreiber für jede Auszahlungsanfrage eines Affiliates verschickt.'
  540.             ],
  541.             [
  542.                 'customer' => 'customer',
  543.                 'salesChannel' => 'sales_channel'
  544.             ], $context);
  545.         // To store owner: New transaction
  546.         $this->addEmailTemplate(
  547.             [
  548.                 'en-GB' => 'Affiliate Marketing (Admin): New transaction',
  549.                 'de-DE' => 'Affiliate-Marketing (Admin): Neue Transaktion'
  550.             ],
  551.             self::ADMIN_NEW_TRANSACTION_TEMPLATE_TYPE_TECHNICAL_NAME,
  552.             [
  553.                 'en-GB' => 'A new affiliate provision has been recorded',
  554.                 'de-DE' => 'Eine neue Affiliate-Provision wurde erfasst'
  555.             ],
  556.             [
  557.                 'en-GB' => 'This template is sent to the store owner for each new provision by an affiliate.',
  558.                 'de-DE' => 'Dieses Template wird an den Shopbetreiber für jede neue Provisionserfassung eines Affiliates verschickt.'
  559.             ],
  560.             [
  561.                 'customer' => 'customer',
  562.                 'salesChannel' => 'sales_channel'
  563.             ], $context);
  564.         // To affiliate: New transaction
  565.         $this->addEmailTemplate(
  566.             [
  567.                 'en-GB' => 'Affiliate Marketing (Affiliate): New transaction',
  568.                 'de-DE' => 'Affiliate-Marketing (Affiliate): Neue Transaktion'
  569.             ],
  570.             self::AFFILIATE_NEW_TRANSACTION_TEMPLATE_TYPE_TECHNICAL_NAME,
  571.             [
  572.                 'en-GB' => 'A new affiliate provision has been tracked',
  573.                 'de-DE' => 'Eine neue Affiliate-Provision wurde erfasst'
  574.             ],
  575.             [
  576.                 'en-GB' => 'This template is sent to the affiliate for each new provision.',
  577.                 'de-DE' => 'Dieses Template wird an den Affiliate für jede neue Provisionserfassung verschickt.'
  578.             ],
  579.             [
  580.                 'customer' => 'customer',
  581.                 'salesChannel' => 'sales_channel'
  582.             ], $context);
  583.         // To affiliate: Payout document
  584.         $this->addEmailTemplate(
  585.             [
  586.                 'en-GB' => 'Affiliate Marketing (Affiliate): Payout receipt',
  587.                 'de-DE' => 'Affiliate-Marketing (Affiliate): Auszahlungsbeleg'
  588.             ],
  589.             self::AFFILIATE_PAYOUT_DOCUMENT_TEMPLATE_TYPE_TECHNICAL_NAME,
  590.             [
  591.                 'en-GB' => 'Affiliate-Marketing Payout receipt',
  592.                 'de-DE' => 'Affiliate Marketing Auszahlungsbeleg'
  593.             ],
  594.             [
  595.                 'en-GB' => 'This template is sent to the affiliate if payout receipts are enabled.',
  596.                 'de-DE' => 'Dieses Template wird an den Affiliate gesendet, wenn Auszahlungsbelege aktiviert sind.'
  597.             ],
  598.             [
  599.                 'customer' => 'customer'
  600.             ], $context);
  601.         // To custom receiver: SEPA XML
  602.         $this->addEmailTemplate(
  603.             [
  604.                 'en-GB' => 'Affiliate Marketing (Affiliate): SEPA XML',
  605.                 'de-DE' => 'Affiliate-Marketing (Affiliate): SEPA XML'
  606.             ],
  607.             self::AFFILIATE_SEPA_XML_TEMPLATE_TYPE_TECHNICAL_NAME,
  608.             [
  609.                 'en-GB' => 'SEPA payout document for affiliate provisions',
  610.                 'de-DE' => 'SEPA Auszahlungsbeleg für Affiliate-Provisionen'
  611.             ],
  612.             [
  613.                 'en-GB' => 'This template is sent if SEPA XML documents are enabled.',
  614.                 'de-DE' => 'Dieses Template wird gesendet, wenn SEPA XML-Dokumente aktiviert sind.'
  615.             ],
  616.             [], $context);
  617.     }
  618.     /**
  619.      * Adds a single email template, if it does not already exist
  620.      * @param $templateTypeName
  621.      * @param $templateTypeTechnicalName
  622.      * @param $mailSubject
  623.      * @param $mailDescription
  624.      * @param $availableEntities
  625.      * @param Context $context
  626.      * @return void
  627.      */
  628.     public function addEmailTemplate($templateTypeName$templateTypeTechnicalName$mailSubject$mailDescription$availableEntitiesContext $context)
  629.     {
  630.         /** @var EntityRepositoryInterface $mailTemplateTypeRepository */
  631.         $mailTemplateTypeRepository $this->container->get('mail_template_type.repository');
  632.         /** @var EntityRepositoryInterface $mailTemplateRepository */
  633.         $mailTemplateRepository $this->container->get('mail_template.repository');
  634.         $mailTemplateTypeId Uuid::randomHex();
  635.         $mailTemplateType = [
  636.             [
  637.                 'id' => $mailTemplateTypeId,
  638.                 'name' => $templateTypeName,
  639.                 'technicalName' => $templateTypeTechnicalName,
  640.                 'availableEntities' => $availableEntities
  641.             ]
  642.         ];
  643.         // Add fallback
  644.         $defaultLocaleCode $this->getDefaultLocaleCode($context);
  645.         if (!in_array($defaultLocaleCode, ['de-DE''en-GB'])) {
  646.             $mailTemplateType[0]['name'][$defaultLocaleCode] = $templateTypeName['en-GB'];
  647.         }
  648.         $mailTemplate = [
  649.             [
  650.                 'id' => Uuid::randomHex(),
  651.                 'mailTemplateTypeId' => $mailTemplateTypeId,
  652.                 'senderName' => [
  653.                     'en-GB' => '{{ salesChannel.translated.name }}',
  654.                     'de-DE' => '{{ salesChannel.translated.name }}'
  655.                 ],
  656.                 'subject' => $mailSubject,
  657.                 'description' => $mailDescription,
  658.                 'contentPlain' => [
  659.                     'en-GB' => $this->getMailContent('en-GB'$templateTypeTechnicalName'plain'),
  660.                     'de-DE' => $this->getMailContent('de-DE'$templateTypeTechnicalName'plain')
  661.                 ],
  662.                 'contentHtml' => [
  663.                     'en-GB' => $this->getMailContent('en-GB'$templateTypeTechnicalName'html'),
  664.                     'de-DE' => $this->getMailContent('de-DE'$templateTypeTechnicalName'html')
  665.                 ]
  666.             ]
  667.         ];
  668.         // Add fallback
  669.         if (!in_array($defaultLocaleCode, ['de-DE''en-GB'])) {
  670.             $mailTemplate[0]['senderName'][$defaultLocaleCode] = '{{ salesChannel.translated.name }}';
  671.             $mailTemplate[0]['subject'][$defaultLocaleCode] = $mailSubject['en-GB'];
  672.             $mailTemplate[0]['description'][$defaultLocaleCode] = $mailDescription['en-GB'];
  673.             $mailTemplate[0]['contentPlain'][$defaultLocaleCode] = $this->getMailContent('en-GB'$templateTypeTechnicalName'plain');
  674.             $mailTemplate[0]['contentHtml'][$defaultLocaleCode] = $this->getMailContent('en-GB'$templateTypeTechnicalName'html');
  675.         }
  676.         try {
  677.             $mailTemplateTypeRepository->create($mailTemplateType$context);
  678.             $mailTemplateRepository->create($mailTemplate$context);
  679.         } catch (UniqueConstraintViolationException $exception) {}
  680.     }
  681.     /**
  682.      * Gets the default locale code
  683.      * @param $context
  684.      * @return string
  685.      */
  686.     private function getDefaultLocaleCode($context): string {
  687.         /** @var EntityRepository $languageRepository */
  688.         $languageRepository $this->container->get('language.repository');
  689.         $criteria = new Criteria();
  690.         $criteria->addAssociation('locale');
  691.         $criteria->addFilter(new EqualsFilter('id'Defaults::LANGUAGE_SYSTEM));
  692.         /** @var LanguageEntity $languageEntity */
  693.         $languageEntity $languageRepository->search($criteria$context)->get(Defaults::LANGUAGE_SYSTEM);
  694.         return $languageEntity->getLocale()->getCode();
  695.     }
  696.     /**
  697.      * Gets the content of a mail while considering the locale
  698.      * @param string $locale
  699.      * @param string $prefix
  700.      * @param string $type
  701.      * @return string
  702.      */
  703.     private function getMailContent(string $localestring $prefixstring $type): string {
  704.         $path $this->getPath() . '/Resources/email/' $locale '/';
  705.         switch ($type) {
  706.             case 'html':
  707.                 $ext 'html';
  708.                 break;
  709.             case 'plain':
  710.                 $ext 'txt';
  711.                 break;
  712.             default:
  713.                 $ext 'txt';
  714.         }
  715.         $file $path $prefix '.' $ext;
  716.         if (!is_file($file)) {
  717.             throw new FileNotFoundException($file);
  718.         }
  719.         return file_get_contents($file);
  720.     }
  721.     /**
  722.      * Removes a specific email template by templateTypeTechnicalName
  723.      * @param $templateTypeTechnicalName
  724.      * @param Context $context
  725.      * @return void
  726.      */
  727.     public function removeEmailTemplate($templateTypeTechnicalNameContext $context)
  728.     {
  729.         /** @var EntityRepositoryInterface $mailTemplateTypeRepository */
  730.         $mailTemplateTypeRepository $this->container->get('mail_template_type.repository');
  731.         /** @var EntityRepositoryInterface $mailTemplateRepository */
  732.         $mailTemplateRepository $this->container->get('mail_template.repository');
  733.         /** @var MailTemplateTypeEntity $customMailTemplateType */
  734.         $customMailTemplateType $mailTemplateTypeRepository->search(
  735.             (new Criteria())
  736.                 ->addFilter(new EqualsFilter('technicalName'$templateTypeTechnicalName)),
  737.             $context
  738.         )->first();
  739.         if ($customMailTemplateType) {
  740.             $mailTemplateIds $mailTemplateRepository->searchIds(
  741.                 (new Criteria())
  742.                     ->addFilter(new EqualsFilter('mailTemplateTypeId'$customMailTemplateType->getId())),
  743.                 $context
  744.             )->getIds();
  745.             $ids array_map(static function ($id) {
  746.                 return ['id' => $id];
  747.             }, $mailTemplateIds);
  748.             $mailTemplateRepository->delete($ids$context);
  749.             // Delete the TemplateType which were added by this Plugin
  750.             $mailTemplateTypeRepository->delete([
  751.                 ['id' => $customMailTemplateType->getId()]
  752.             ], $context);
  753.         }
  754.     }
  755.     public function removeNumberRange($connectionContext $context)
  756.     {
  757.         /** @var EntityRepositoryInterface $numberRangeTypeRepository */
  758.         $numberRangeTypeRepository $this->container->get('number_range_type.repository');
  759.         /** @var EntityRepositoryInterface $numberRangeTypeRepository */
  760.         $numberRangeRepository $this->container->get('number_range.repository');
  761.         /** @var NumberRangeTypeEntity $numberRangeType */
  762.         $numberRangeType $numberRangeTypeRepository->search(
  763.             (new Criteria())
  764.                 ->addFilter(new EqualsFilter('technicalName'self::AFFILIATE_PAYOUT_NUMBER_RANGE)),
  765.             $context
  766.         )->getEntities()->first();
  767.         if ($numberRangeType) {
  768.             $numberRangeTypeId $numberRangeType->getId();
  769.             $connection->executeStatement('
  770.             DELETE FROM `number_range`
  771.             WHERE `type_id` = UNHEX(\'' $numberRangeTypeId '\')
  772.             ');
  773.             /** @var NumberRangeEntity $numberRange */
  774.             $numberRange $numberRangeRepository->search(
  775.                 (new Criteria())
  776.                     ->addFilter(new EqualsFilter('typeId'$numberRangeTypeId)),
  777.                 $context
  778.             )->getEntities()->first();
  779.             if ($numberRange) {
  780.                 $numberRangeId $numberRange->getId();
  781.                 $connection->executeStatement('
  782.             DELETE FROM `number_range_state`
  783.             WHERE `number_range_id` = UNHEX(\'' $numberRangeId '\')
  784.             ');
  785.             }
  786.             $connection->executeStatement('
  787.             DELETE FROM `number_range_type`
  788.             WHERE `technical_name` LIKE \'%' self::AFFILIATE_PAYOUT_NUMBER_RANGE '%\'
  789.             ');
  790.         }
  791.     }
  792. }