diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd9f04a..2b0d270 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,11 +109,6 @@ jobs: --health-timeout=5s --health-retries=3 - mailhog: - image: mailhog/mailhog - ports: - - 1025:1025 - steps: - name: Checkout plugin repository uses: actions/checkout@v6 @@ -151,7 +146,7 @@ jobs: DATABASE_URL: ${{ matrix.database == 'mysql' && 'mysql://root:root@127.0.0.1:3306/eccube_test' || 'postgresql://postgres:postgres@127.0.0.1:5432/eccube_test' }} DATABASE_SERVER_VERSION: ${{ matrix.database == 'mysql' && '8.0' || '15' }} DATABASE_CHARSET: ${{ matrix.database == 'mysql' && 'utf8mb4' || 'UTF8' }} - MAILER_DSN: 'smtp://127.0.0.1:1025' + MAILER_DSN: 'null://null' run: | php bin/console doctrine:database:create --if-not-exists php bin/console doctrine:schema:create @@ -167,7 +162,7 @@ jobs: DATABASE_URL: ${{ matrix.database == 'mysql' && 'mysql://root:root@127.0.0.1:3306/eccube_test' || 'postgresql://postgres:postgres@127.0.0.1:5432/eccube_test' }} DATABASE_SERVER_VERSION: ${{ matrix.database == 'mysql' && '8.0' || '15' }} DATABASE_CHARSET: ${{ matrix.database == 'mysql' && 'utf8mb4' || 'UTF8' }} - MAILER_DSN: 'smtp://127.0.0.1:1025' + MAILER_DSN: 'null://null' run: | vendor/bin/phpunit --exclude-group cache-clear,cache-clear-install,update-schema-doctrine,plugin-service app/Plugin/StockAlertMail/Tests/ diff --git a/Command/StockAlertCommand.php b/Command/StockAlertCommand.php index ba82c5e..09dd7c9 100644 --- a/Command/StockAlertCommand.php +++ b/Command/StockAlertCommand.php @@ -54,7 +54,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $config = $this->configRepository->findOneBy([]); + $config = $this->configRepository->find(1); if ($config === null) { $io->error($this->translator->trans('stock_alert_mail.command.config_not_found')); @@ -71,6 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int ->andWhere('pc.visible = true') ->andWhere('p.Status = :status') ->setParameter('status', ProductStatus::DISPLAY_SHOW) + ->orderBy('p.id', 'ASC') ->getQuery() ->getResult(); @@ -108,12 +109,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->info($this->translator->trans('stock_alert_mail.command.alert_items_found', ['%count%' => count($newAlertItems)])); // メール送信 - $BaseInfo = $this->mailBuilder->getBaseInfo(); - $toEmails = $this->mailBuilder->resolveToEmails($config); - $body = $this->mailBuilder->buildMailBody($config, $newAlertItems, $threshold); - $subject = $this->mailBuilder->buildMailSubject($config); - try { + $BaseInfo = $this->mailBuilder->getBaseInfo(); + $toEmails = $this->mailBuilder->resolveToEmails($config); + $body = $this->mailBuilder->buildMailBody($newAlertItems, $threshold); + $subject = $this->mailBuilder->buildMailSubject(); + $message = (new Email()) ->subject($subject) ->from(new Address($BaseInfo->getEmail01(), $BaseInfo->getShopName())) @@ -135,7 +136,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->entityManager->flush(); $io->success($this->translator->trans('stock_alert_mail.command.mail_sent', ['%emails%' => implode(', ', $toEmails)])); - } catch (\Exception $e) { + } catch (\Throwable $e) { $io->error($this->translator->trans('stock_alert_mail.command.mail_failed', ['%message%' => $e->getMessage()])); return Command::FAILURE; diff --git a/Controller/Admin/StockAlertConfigController.php b/Controller/Admin/StockAlertConfigController.php index 39975ea..08da3fc 100644 --- a/Controller/Admin/StockAlertConfigController.php +++ b/Controller/Admin/StockAlertConfigController.php @@ -17,15 +17,21 @@ use Plugin\StockAlertMail\Entity\StockAlertConfig; use Plugin\StockAlertMail\Form\Type\Admin\StockAlertConfigType; use Plugin\StockAlertMail\Repository\StockAlertConfigRepository; +use Plugin\StockAlertMail\Service\StockAlertMailBuilder; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; use Symfony\Component\Routing\Annotation\Route; class StockAlertConfigController extends AbstractController { public function __construct( private readonly StockAlertConfigRepository $configRepository, + private readonly StockAlertMailBuilder $mailBuilder, + private readonly MailerInterface $mailer, ) { } @@ -36,7 +42,7 @@ public function __construct( */ public function index(Request $request): array|Response { - $config = $this->configRepository->findOneBy([]); + $config = $this->configRepository->find(1); if ($config === null) { $config = new StockAlertConfig(); $config->setCreateDate(new \DateTime()); @@ -59,4 +65,48 @@ public function index(Request $request): array|Response 'form' => $form->createView(), ]; } + + /** + * @Route("/%eccube_admin_route%/plugin/stock-alert/config/send-test", name="stock_alert_mail_admin_config_send_test", methods={"POST"}) + */ + public function sendTest(Request $request): Response + { + if (!$this->isCsrfTokenValid('stock_alert_mail_send_test', $request->request->get('_token'))) { + $this->addError('admin.common.csrf_error', 'admin'); + + return $this->redirectToRoute('stock_alert_mail_admin_config'); + } + + $config = $this->configRepository->find(1); + if ($config === null) { + $this->addError('stock_alert_mail.admin.config.send_test.config_not_found', 'admin'); + + return $this->redirectToRoute('stock_alert_mail_admin_config'); + } + + $BaseInfo = $this->mailBuilder->getBaseInfo(); + $toEmails = $this->mailBuilder->resolveToEmails($config); + $dummyItems = $this->mailBuilder->createDummyItems(); + $subject = '[TEST] '.$this->mailBuilder->buildMailSubject(); + $body = $this->mailBuilder->buildMailBody($dummyItems, $config->getThreshold()); + + try { + $message = (new Email()) + ->subject($subject) + ->from(new Address($BaseInfo->getEmail01(), $BaseInfo->getShopName())) + ->text($body); + + foreach ($toEmails as $email) { + $message->addTo($email); + } + + $this->mailer->send($message); + + $this->addSuccess($this->translator->trans('stock_alert_mail.admin.config.send_test.success', ['%emails%' => implode(', ', $toEmails)]), 'admin'); + } catch (\Exception $e) { + $this->addError($this->translator->trans('stock_alert_mail.admin.config.send_test.failed', ['%message%' => $e->getMessage()]), 'admin'); + } + + return $this->redirectToRoute('stock_alert_mail_admin_config'); + } } diff --git a/Controller/Admin/StockAlertMailTemplateController.php b/Controller/Admin/StockAlertMailTemplateController.php deleted file mode 100644 index eab44d9..0000000 --- a/Controller/Admin/StockAlertMailTemplateController.php +++ /dev/null @@ -1,112 +0,0 @@ -configRepository->findOneBy([]); - if ($config === null) { - $config = new StockAlertConfig(); - $config->setCreateDate(new \DateTime()); - } - - $form = $this->createForm(StockAlertMailTemplateType::class, $config); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $config->setUpdateDate(new \DateTime()); - $this->entityManager->persist($config); - $this->entityManager->flush(); - - $this->addSuccess('stock_alert_mail.admin.config.save_success', 'admin'); - - return $this->redirectToRoute('stock_alert_mail_admin_mail_template'); - } - - return [ - 'form' => $form->createView(), - ]; - } - - /** - * @Route("/%eccube_admin_route%/plugin/stock-alert/mail-template/send-test", name="stock_alert_mail_admin_mail_template_send_test", methods={"POST"}) - */ - public function sendTest(Request $request): Response - { - if (!$this->isCsrfTokenValid('stock_alert_mail_send_test', $request->request->get('_token'))) { - $this->addError('admin.common.csrf_error', 'admin'); - - return $this->redirectToRoute('stock_alert_mail_admin_mail_template'); - } - - $config = $this->configRepository->findOneBy([]); - if ($config === null) { - $this->addError('stock_alert_mail.admin.mail_template.send_test.config_not_found', 'admin'); - - return $this->redirectToRoute('stock_alert_mail_admin_mail_template'); - } - - $BaseInfo = $this->mailBuilder->getBaseInfo(); - $dummyItems = $this->mailBuilder->createDummyItems(); - $toEmails = $this->mailBuilder->resolveToEmails($config); - $subject = '[TEST] '.$this->mailBuilder->buildMailSubject($config); - $body = $this->mailBuilder->buildMailBody($config, $dummyItems, $config->getThreshold()); - - try { - $message = (new Email()) - ->subject($subject) - ->from(new Address($BaseInfo->getEmail01(), $BaseInfo->getShopName())) - ->text($body); - - foreach ($toEmails as $email) { - $message->addTo($email); - } - - $this->mailer->send($message); - - $this->addSuccess($this->translator->trans('stock_alert_mail.admin.mail_template.send_test.success', ['%emails%' => implode(', ', $toEmails)]), 'admin'); - } catch (\Exception $e) { - $this->addError($this->translator->trans('stock_alert_mail.admin.mail_template.send_test.failed', ['%message%' => $e->getMessage()]), 'admin'); - } - - return $this->redirectToRoute('stock_alert_mail_admin_mail_template'); - } -} diff --git a/DoctrineMigrations/Version20260320000000.php b/DoctrineMigrations/Version20260320000000.php index 06fb4dd..4a480a7 100644 --- a/DoctrineMigrations/Version20260320000000.php +++ b/DoctrineMigrations/Version20260320000000.php @@ -32,8 +32,6 @@ public function up(Schema $schema): void $table->addColumn('id', 'integer', ['unsigned' => true, 'autoincrement' => true]); $table->addColumn('threshold', 'integer', ['default' => 5]); $table->addColumn('alert_emails', 'string', ['length' => 1000, 'notnull' => false]); - $table->addColumn('mail_subject', 'string', ['length' => 500, 'notnull' => false]); - $table->addColumn('mail_body', 'text', ['notnull' => false]); $table->addColumn('create_date', 'datetimetz'); $table->addColumn('update_date', 'datetimetz'); $table->setPrimaryKey(['id']); diff --git a/DoctrineMigrations/Version20260321000000.php b/DoctrineMigrations/Version20260321000000.php new file mode 100644 index 0000000..54a6a2e --- /dev/null +++ b/DoctrineMigrations/Version20260321000000.php @@ -0,0 +1,62 @@ +hasTable(self::TABLE)) { + return; + } + + $table = $schema->getTable(self::TABLE); + + if ($table->hasColumn('mail_subject')) { + $table->dropColumn('mail_subject'); + } + + if ($table->hasColumn('mail_body')) { + $table->dropColumn('mail_body'); + } + } + + public function down(Schema $schema): void + { + if (!$schema->hasTable(self::TABLE)) { + return; + } + + $table = $schema->getTable(self::TABLE); + + if (!$table->hasColumn('mail_subject')) { + $table->addColumn('mail_subject', 'string', ['length' => 500, 'notnull' => false]); + } + + if (!$table->hasColumn('mail_body')) { + $table->addColumn('mail_body', 'text', ['notnull' => false]); + } + } +} diff --git a/Entity/StockAlertConfig.php b/Entity/StockAlertConfig.php index aecf7f9..35e6826 100644 --- a/Entity/StockAlertConfig.php +++ b/Entity/StockAlertConfig.php @@ -29,9 +29,9 @@ class StockAlertConfig * * @ORM\Id * - * @ORM\GeneratedValue(strategy="IDENTITY") + * @ORM\GeneratedValue(strategy="NONE") */ - private $id; + private $id = 1; /** * 在庫アラート閾値(この数以下になったら通知) @@ -47,22 +47,6 @@ class StockAlertConfig */ private $alertEmails; - /** - * メール件名テンプレート(空の場合はデフォルトを使用) - * 使用可能プレースホルダー: {shop_name} - * - * @ORM\Column(name="mail_subject", type="string", length=500, nullable=true) - */ - private $mailSubject; - - /** - * メール本文テンプレート(空の場合はデフォルトを使用) - * 使用可能プレースホルダー: {shop_name}, {threshold}, {items} - * - * @ORM\Column(name="mail_body", type="text", nullable=true) - */ - private $mailBody; - /** * @ORM\Column(name="create_date", type="datetimetz") */ @@ -102,30 +86,6 @@ public function setAlertEmails($alertEmails) return $this; } - public function getMailSubject() - { - return $this->mailSubject; - } - - public function setMailSubject($mailSubject) - { - $this->mailSubject = $mailSubject; - - return $this; - } - - public function getMailBody() - { - return $this->mailBody; - } - - public function setMailBody($mailBody) - { - $this->mailBody = $mailBody; - - return $this; - } - public function getCreateDate() { return $this->createDate; diff --git a/Form/Type/Admin/StockAlertMailTemplateType.php b/Form/Type/Admin/StockAlertMailTemplateType.php deleted file mode 100644 index 3ff7a8d..0000000 --- a/Form/Type/Admin/StockAlertMailTemplateType.php +++ /dev/null @@ -1,63 +0,0 @@ -add('mailSubject', TextType::class, [ - 'label' => 'stock_alert_mail.form.mail_subject.label', - 'required' => false, - 'constraints' => [ - new Assert\Length(['max' => 500]), - new Assert\Regex([ - 'pattern' => '/[\r\n]/', - 'match' => false, - 'message' => '件名に改行を含めることはできません。', - ]), - ], - 'attr' => [ - 'placeholder' => 'stock_alert_mail.form.mail_subject.placeholder', - ], - ]) - ->add('mailBody', TextareaType::class, [ - 'label' => 'stock_alert_mail.form.mail_body.label', - 'required' => false, - 'constraints' => [ - new Assert\Length(['max' => 10000]), - ], - 'attr' => [ - 'rows' => 20, - 'style' => 'font-family: monospace;', - ], - ]); - } - - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'data_class' => StockAlertConfig::class, - ]); - } -} diff --git a/Nav.php b/Nav.php index 247ffd8..dc0997e 100644 --- a/Nav.php +++ b/Nav.php @@ -31,10 +31,6 @@ public static function getNav(): array 'name' => 'stock_alert_mail.nav.config', 'url' => 'stock_alert_mail_admin_config', ], - 'stock_alert_mail_mail_template' => [ - 'name' => 'stock_alert_mail.nav.mail_template', - 'url' => 'stock_alert_mail_admin_mail_template', - ], 'stock_alert_mail_log' => [ 'name' => 'stock_alert_mail.nav.log', 'url' => 'stock_alert_mail_admin_log', diff --git a/PluginManager.php b/PluginManager.php index 572dcb3..485270c 100644 --- a/PluginManager.php +++ b/PluginManager.php @@ -14,21 +14,32 @@ namespace Plugin\StockAlertMail; use Doctrine\ORM\EntityManagerInterface; +use Eccube\Common\EccubeConfig; +use Eccube\Entity\MailTemplate; use Eccube\Plugin\AbstractPluginManager; use Plugin\StockAlertMail\Entity\StockAlertConfig; use Psr\Container\ContainerInterface; class PluginManager extends AbstractPluginManager { + /** dtb_mail_template に登録するファイル名 */ + public const MAIL_TEMPLATE_FILE_NAME = 'Mail/stock_alert.twig'; + + /** プラグインのデフォルトメール件名(送信時に "[ショップ名] " が先頭に付く) */ + private const MAIL_SUBJECT = '在庫アラート通知'; + public function enable(array $meta, ContainerInterface $container): void { $entityManager = $container->get('doctrine')->getManager(); $this->createInitialConfig($entityManager); + $this->createMailTemplate($entityManager, $container); } public function uninstall(array $meta, ContainerInterface $container): void { $entityManager = $container->get('doctrine')->getManager(); + $this->deleteMailTemplate($entityManager, $container); + $conn = $entityManager->getConnection(); $schemaManager = $conn->createSchemaManager(); @@ -45,8 +56,7 @@ private function createInitialConfig(EntityManagerInterface $entityManager): voi { $repository = $entityManager->getRepository(StockAlertConfig::class); - // 既に設定が存在する場合はスキップ - if ($repository->findOneBy([]) !== null) { + if ($repository->find(1) !== null) { return; } @@ -58,4 +68,81 @@ private function createInitialConfig(EntityManagerInterface $entityManager): voi $entityManager->persist($config); $entityManager->flush(); } + + private function createMailTemplate(EntityManagerInterface $entityManager, ContainerInterface $container): void + { + // まずテンプレート実体を保証(欠落時の自己修復) + $this->copyTwigTemplate($container); + + $repository = $entityManager->getRepository(MailTemplate::class); + + // 既に登録済みの場合はDB登録のみスキップ + if ($repository->findOneBy(['file_name' => self::MAIL_TEMPLATE_FILE_NAME]) !== null) { + return; + } + + $mailTemplate = new MailTemplate(); + $mailTemplate->setName('在庫アラートメール'); + $mailTemplate->setFileName(self::MAIL_TEMPLATE_FILE_NAME); + $mailTemplate->setMailSubject(self::MAIL_SUBJECT); + $mailTemplate->setCreateDate(new \DateTime()); + $mailTemplate->setUpdateDate(new \DateTime()); + + $entityManager->persist($mailTemplate); + $entityManager->flush(); + } + + private function deleteMailTemplate(EntityManagerInterface $entityManager, ContainerInterface $container): void + { + $repository = $entityManager->getRepository(MailTemplate::class); + $mailTemplate = $repository->findOneBy(['file_name' => self::MAIL_TEMPLATE_FILE_NAME]); + + if ($mailTemplate !== null) { + $entityManager->remove($mailTemplate); + $entityManager->flush(); + } + + $this->removeTwigTemplate($container); + } + + private function copyTwigTemplate(ContainerInterface $container): void + { + $targetPath = $this->getTwigTargetPath($container); + + if (file_exists($targetPath)) { + return; + } + + $sourcePath = __DIR__.'/Resource/template/Mail/stock_alert.twig'; + + if (!file_exists($sourcePath)) { + throw new \RuntimeException(sprintf('プラグインのテンプレートファイルが見つかりません: %s', $sourcePath)); + } + + $targetDir = dirname($targetPath); + if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true)) { + throw new \RuntimeException(sprintf('テンプレートディレクトリの作成に失敗しました: %s', $targetDir)); + } + + if (!copy($sourcePath, $targetPath)) { + throw new \RuntimeException(sprintf('テンプレートのコピーに失敗しました: %s → %s', $sourcePath, $targetPath)); + } + } + + private function removeTwigTemplate(ContainerInterface $container): void + { + $targetPath = $this->getTwigTargetPath($container); + + if (file_exists($targetPath)) { + unlink($targetPath); + } + } + + private function getTwigTargetPath(ContainerInterface $container): string + { + /** @var EccubeConfig $eccubeConfig */ + $eccubeConfig = $container->get(EccubeConfig::class); + + return $eccubeConfig['eccube_theme_front_dir'].'/'.self::MAIL_TEMPLATE_FILE_NAME; + } } diff --git a/Resource/locale/messages.en.yaml b/Resource/locale/messages.en.yaml index 77e83f0..302047c 100644 --- a/Resource/locale/messages.en.yaml +++ b/Resource/locale/messages.en.yaml @@ -11,24 +11,21 @@ stock_alert_mail.admin.config.cron.note: The example above runs at every hour on stock_alert_mail.admin.config.unit: unit(s) stock_alert_mail.admin.config.save: Save stock_alert_mail.admin.config.save_success: Saved successfully. +stock_alert_mail.admin.config.send_test.button: Send Test Email +stock_alert_mail.admin.config.send_test.confirm: Send a test email? (Uses dummy product data) +stock_alert_mail.admin.config.send_test.success: 'Test email sent to %emails%.' +stock_alert_mail.admin.config.send_test.failed: 'Failed to send test email: %message%' +stock_alert_mail.admin.config.send_test.config_not_found: Settings not found. stock_alert_mail.admin.config.required: Required stock_alert_mail.admin.config.section.mail_template: Mail Template -stock_alert_mail.admin.config.mail_template.description: If left empty, the default template will be used. -stock_alert_mail.admin.config.mail_template.placeholders: Available placeholders -stock_alert_mail.admin.config.mail_template.placeholder.shop_name: Shop name -stock_alert_mail.admin.config.mail_template.placeholder.threshold: Threshold (units) -stock_alert_mail.admin.config.mail_template.placeholder.items: List of low-stock products -stock_alert_mail.admin.config.mail_template.subject_help: 'Default if empty: [{shop_name}] Stock Alert Notification' -stock_alert_mail.admin.config.mail_template.body_help: If empty, the default body will be used. +stock_alert_mail.admin.config.mail_template.description: The mail subject and body can be edited from the standard EC-CUBE mail settings. +stock_alert_mail.admin.config.mail_template.link: Open mail settings stock_alert_mail.form.threshold.label: Stock Alert Threshold stock_alert_mail.form.threshold.placeholder: 'e.g. 5' stock_alert_mail.form.threshold.help: Products at or below this quantity will trigger an email notification. stock_alert_mail.form.alert_emails.label: Notification Email Address(es) stock_alert_mail.form.alert_emails.placeholder: If empty, the store email address will be used. Separate multiple addresses with commas. -stock_alert_mail.form.mail_subject.label: Mail Subject -stock_alert_mail.form.mail_subject.placeholder: '[{shop_name}] Stock Alert Notification' -stock_alert_mail.form.mail_body.label: Mail Body stock_alert_mail.command.description: Notifies the administrator by email when stock falls below the threshold stock_alert_mail.command.config_not_found: Plugin configuration not found. Please enable the plugin. @@ -36,35 +33,21 @@ stock_alert_mail.command.no_alert_items: No new low-stock items found. stock_alert_mail.command.alert_items_found: 'Found %count% new low-stock item(s).' stock_alert_mail.command.mail_sent: 'Stock alert email sent to %emails%.' stock_alert_mail.command.mail_failed: 'Failed to send email: %message%' -stock_alert_mail.command.mail_subject: '[%shop_name%] Stock Alert Notification' stock_alert_mail.mail.greeting: 'Dear %shop_name% Administrator,' stock_alert_mail.mail.low_stock_intro: 'The following products have stock at or below the threshold (%threshold% units).' stock_alert_mail.mail.please_check: Please review and restock as needed. stock_alert_mail.mail.alert_title: 'Low Stock Items (%count% item(s))' +stock_alert_mail.mail.product_id: 'Product ID: %id%' +stock_alert_mail.mail.product_id_label: '(Product ID: %id%)' stock_alert_mail.mail.current_stock: 'Current Stock: %stock% unit(s)' stock_alert_mail.mail.threshold_label: 'Threshold: %threshold% unit(s)' stock_alert_mail.mail.restock_message: Please consider restocking these items. stock_alert_mail.nav.title: Stock Alert stock_alert_mail.nav.config: Settings -stock_alert_mail.nav.mail_template: Mail Template stock_alert_mail.nav.log: Send History -stock_alert_mail.admin.mail_template.title: Mail Template -stock_alert_mail.admin.mail_template.section.placeholders: Available Placeholders -stock_alert_mail.admin.mail_template.section.template: Edit Template -stock_alert_mail.admin.mail_template.placeholder.description: The following placeholders can be used in the template. -stock_alert_mail.admin.mail_template.placeholder.col.key: Placeholder -stock_alert_mail.admin.mail_template.placeholder.col.description: Description -stock_alert_mail.admin.mail_template.placeholder.col.subject: Usable in Subject - -stock_alert_mail.admin.mail_template.send_test.button: Send Test Email -stock_alert_mail.admin.mail_template.send_test.confirm: Send a test email? (Uses dummy product data) -stock_alert_mail.admin.mail_template.send_test.success: 'Test email sent to %emails%.' -stock_alert_mail.admin.mail_template.send_test.failed: 'Failed to send test email: %message%' -stock_alert_mail.admin.mail_template.send_test.config_not_found: Settings not found. Please save settings first. - stock_alert_mail.test.dummy_product_a: Sample Product A stock_alert_mail.test.dummy_product_b: Sample Product B stock_alert_mail.test.dummy_class1: Red diff --git a/Resource/locale/messages.ja.yaml b/Resource/locale/messages.ja.yaml index 82859db..50893c6 100644 --- a/Resource/locale/messages.ja.yaml +++ b/Resource/locale/messages.ja.yaml @@ -11,24 +11,21 @@ stock_alert_mail.admin.config.cron.note: 上記の例は毎時0分に実行し stock_alert_mail.admin.config.unit: 個 stock_alert_mail.admin.config.save: 保存 stock_alert_mail.admin.config.save_success: 保存しました。 +stock_alert_mail.admin.config.send_test.button: テストメール送信 +stock_alert_mail.admin.config.send_test.confirm: テストメールを送信しますか?(ダミーの商品データで送信されます) +stock_alert_mail.admin.config.send_test.success: 'テストメールを %emails% に送信しました。' +stock_alert_mail.admin.config.send_test.failed: 'テストメール送信に失敗しました: %message%' +stock_alert_mail.admin.config.send_test.config_not_found: 設定が見つかりません。 stock_alert_mail.admin.config.required: 必須 stock_alert_mail.admin.config.section.mail_template: メールテンプレート -stock_alert_mail.admin.config.mail_template.description: 空欄の場合はデフォルトのテンプレートを使用します。 -stock_alert_mail.admin.config.mail_template.placeholders: 使用可能なプレースホルダー -stock_alert_mail.admin.config.mail_template.placeholder.shop_name: 店舗名 -stock_alert_mail.admin.config.mail_template.placeholder.threshold: 閾値(個) -stock_alert_mail.admin.config.mail_template.placeholder.items: 在庫アラート対象商品の一覧 -stock_alert_mail.admin.config.mail_template.subject_help: '空欄の場合のデフォルト: [{shop_name}] 在庫アラート通知' -stock_alert_mail.admin.config.mail_template.body_help: 空欄の場合はデフォルトの本文を使用します。 +stock_alert_mail.admin.config.mail_template.description: メールの件名・本文はEC-CUBE標準のメール設定画面で編集できます。 +stock_alert_mail.admin.config.mail_template.link: メール設定を開く stock_alert_mail.form.threshold.label: 在庫アラート閾値 stock_alert_mail.form.threshold.placeholder: '例: 5' stock_alert_mail.form.threshold.help: この個数以下になった商品をメールで通知します。 stock_alert_mail.form.alert_emails.label: 通知先メールアドレス stock_alert_mail.form.alert_emails.placeholder: 空欄の場合は店舗設定のメールアドレスを使用します。複数の場合はカンマ区切り。 -stock_alert_mail.form.mail_subject.label: メール件名 -stock_alert_mail.form.mail_subject.placeholder: '[{shop_name}] 在庫アラート通知' -stock_alert_mail.form.mail_body.label: メール本文 stock_alert_mail.command.description: 在庫数が閾値以下の商品を管理者にメール通知します stock_alert_mail.command.config_not_found: プラグイン設定が見つかりません。プラグインを有効化してください。 @@ -36,35 +33,21 @@ stock_alert_mail.command.no_alert_items: 新規の在庫アラート対象商品 stock_alert_mail.command.alert_items_found: '%count% 件の新規在庫アラート対象商品が見つかりました。' stock_alert_mail.command.mail_sent: '在庫アラートメールを %emails% に送信しました。' stock_alert_mail.command.mail_failed: 'メール送信に失敗しました: %message%' -stock_alert_mail.command.mail_subject: '[%shop_name%] 在庫アラート通知' stock_alert_mail.mail.greeting: '%shop_name% 管理者様' stock_alert_mail.mail.low_stock_intro: '在庫数が閾値(%threshold%個)以下の商品があります。' stock_alert_mail.mail.please_check: ご確認をお願いします。 stock_alert_mail.mail.alert_title: '在庫アラート対象商品(%count%件)' +stock_alert_mail.mail.product_id: '商品ID: %id%' +stock_alert_mail.mail.product_id_label: '(商品ID: %id%)' stock_alert_mail.mail.current_stock: '現在の在庫数:%stock% 個' stock_alert_mail.mail.threshold_label: '閾値:%threshold% 個' stock_alert_mail.mail.restock_message: 在庫の補充をご検討ください。 stock_alert_mail.nav.title: 在庫アラート stock_alert_mail.nav.config: 設定 -stock_alert_mail.nav.mail_template: メールテンプレート stock_alert_mail.nav.log: 送信履歴 -stock_alert_mail.admin.mail_template.title: メールテンプレート -stock_alert_mail.admin.mail_template.section.placeholders: 使用可能なプレースホルダー -stock_alert_mail.admin.mail_template.section.template: テンプレート編集 -stock_alert_mail.admin.mail_template.placeholder.description: 以下のプレースホルダーをテンプレート内で使用できます。 -stock_alert_mail.admin.mail_template.placeholder.col.key: プレースホルダー -stock_alert_mail.admin.mail_template.placeholder.col.description: 内容 -stock_alert_mail.admin.mail_template.placeholder.col.subject: 件名で使用可 - -stock_alert_mail.admin.mail_template.send_test.button: テストメール送信 -stock_alert_mail.admin.mail_template.send_test.confirm: テストメールを送信しますか?(ダミーの商品データで送信されます) -stock_alert_mail.admin.mail_template.send_test.success: 'テストメールを %emails% に送信しました。' -stock_alert_mail.admin.mail_template.send_test.failed: 'テストメール送信に失敗しました: %message%' -stock_alert_mail.admin.mail_template.send_test.config_not_found: 設定が保存されていません。先に設定画面で保存してください。 - stock_alert_mail.test.dummy_product_a: サンプル商品A stock_alert_mail.test.dummy_product_b: サンプル商品B stock_alert_mail.test.dummy_class1: レッド diff --git a/Resource/template/Mail/stock_alert.twig b/Resource/template/Mail/stock_alert.twig index a004dde..4f3f7aa 100644 --- a/Resource/template/Mail/stock_alert.twig +++ b/Resource/template/Mail/stock_alert.twig @@ -7,11 +7,13 @@ {{ 'stock_alert_mail.mail.alert_title'|trans({'%count%': lowStockItems|length}) }} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +{% set currentProductId = null %} {% for item in lowStockItems %} -■ {{ item.Product.name }}{% if item.hasClassCategory1 %} [{{ item.ClassCategory1.name }}{% if item.hasClassCategory2 %} / {{ item.ClassCategory2.name }}{% endif %}]{% endif %} - - {{ 'stock_alert_mail.mail.current_stock'|trans({'%stock%': item.stock}) }} - {{ 'stock_alert_mail.mail.threshold_label'|trans({'%threshold%': threshold}) }} +{% if item.Product.id != currentProductId %} +{% set currentProductId = item.Product.id %} +■ {{ item.Product.name }} {{ 'stock_alert_mail.mail.product_id_label'|trans({'%id%': item.Product.id}) }} +{% endif %} + - {% if item.hasClassCategory1 %}{{ item.ClassCategory1.name }}{% if item.hasClassCategory2 %} / {{ item.ClassCategory2.name }}{% endif %}{% else %}—{% endif %}:{{ 'stock_alert_mail.mail.current_stock'|trans({'%stock%': item.stock}) }} {% endfor %} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/Resource/template/admin/config.twig b/Resource/template/admin/config.twig index b02819d..05b368a 100644 --- a/Resource/template/admin/config.twig +++ b/Resource/template/admin/config.twig @@ -51,6 +51,17 @@ +
{{ 'stock_alert_mail.admin.config.mail_template.description'|trans }} + {{ 'stock_alert_mail.admin.config.mail_template.link'|trans }} +
+