From: Vladimir Fomichev Date: Mon, 19 Jan 2026 11:09:47 +0000 (+0300) Subject: Отправка почты X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=82181ee2d3e0e4f1da9842cedf699eda743d2f51;p=erp24_rep%2Fyii-erp24%2F.git Отправка почты --- diff --git a/erp24/.env.example b/erp24/.env.example index 5e391bb0..25a6251f 100644 --- a/erp24/.env.example +++ b/erp24/.env.example @@ -1,3 +1,19 @@ APP_ENV=development SERVER_NAME=local-fomichev -RABBIT_HOST=rabbitmq-yii_erp24 \ No newline at end of file +RABBIT_HOST=rabbitmq-yii_erp24 + +# === SMTP Configuration === +# Для отправки email из консольных команд (отчёты, уведомления) +MAIL_SCHEME=smtp +MAIL_HOST=smtp.yandex.ru +MAIL_PORT=465 +MAIL_USERNAME=noreply@bazacvetov24.ru +MAIL_PASSWORD=your_smtp_password_here +MAIL_ENCRYPTION=ssl + +# === Order Control Report (ERP-36J) === +# Telegram chat_id для dev/prod +TELEGRAM_ORDER_CONTROL_CHAT_ID_DEV=-1001861631125 +TELEGRAM_ORDER_CONTROL_CHAT_ID_PROD= +# Email получатели (через запятую) +ORDER_CONTROL_EMAIL_RECIPIENTS=ekaterina.geldak@bazacvetov24.ru,irina.rogacheva@bazacvetov24.ru,alena.chelyshkina@bazacvetov24.ru \ No newline at end of file diff --git a/erp24/config/console.php b/erp24/config/console.php index 5b0fcb51..648d4f34 100755 --- a/erp24/config/console.php +++ b/erp24/config/console.php @@ -53,6 +53,23 @@ $config = [ 'cache' => [ 'class' => 'yii\caching\FileCache', ], + // Mailer для отправки email (консольные команды) + 'mailer' => [ + 'class' => \yii\symfonymailer\Mailer::class, + 'viewPath' => '@app/mail', + // В dev-режиме письма сохраняются в файл, в prod — отправляются через SMTP + //'useFileTransport' => getenv('APP_ENV') === 'development', + 'useFileTransport' => false, + // SMTP-транспорт для production (настраивается через .env) + 'transport' => [ + 'scheme' => getenv('MAIL_SCHEME') ?: 'smtp', + 'host' => getenv('MAIL_HOST') ?: 'smtp.yandex.ru', + 'port' => (int)(getenv('MAIL_PORT') ?: 465), + 'username' => getenv('MAIL_USERNAME') ?: 'flow@bazacvetov24.ru', + 'password' => getenv('MAIL_PASSWORD') ?: 'ctqamxqeshgxwsgn', + 'encryption' => getenv('MAIL_ENCRYPTION') ?: 'ssl', + ], + ], 'log' => [ 'targets' => [ [ diff --git a/erp24/config/params.php b/erp24/config/params.php index 8997e908..b1b88e09 100644 --- a/erp24/config/params.php +++ b/erp24/config/params.php @@ -18,16 +18,21 @@ return [ 'YANDEX_MARKET_API_KEY' => 'ACMA:r3sa2VyjkgcO0aOxGoyAWuGH15g5mWAqXRMuylVA:a0bccb7e', 'RABBIT_HOST' => getenv('RABBIT_HOST') ?: 'localhost', - // Отчёт о непробитых чеках маркетплейсов - 'MARKETPLACE_UNCHECKED_ORDERS_REPORT' => [ + // Отчёт контроля статусов заказов маркетплейсов (ТЗ ERP-36J) + // Расписание: 08:00 и 20:00 MSK + // Типы проблем: "Завис в доставке", "Успех без чека", "Отмена без обработки" + 'MARKETPLACE_ORDER_CONTROL_REPORT' => [ 'period_hours' => 12, 'timezone' => 'Europe/Moscow', 'max_retries' => 3, 'retry_delay_seconds' => 5, 'telegram_max_message_length' => 4000, - 'telegram_chat_id_dev' => getenv('TELEGRAM_UNCHECKED_ORDERS_CHAT_ID_DEV') ?: '-1001861631125', - 'telegram_chat_id_prod' => getenv('TELEGRAM_UNCHECKED_ORDERS_CHAT_ID_PROD') ?: '4886272326', - 'email_recipients' => array_filter(explode(',', getenv('UNCHECKED_ORDERS_EMAIL_RECIPIENTS') ?: 'vladimir.fomichev@erp-flowers.ru')), - 'email_subject' => 'Отчёт о заказах с непробитыми чеками', + // Telegram: chat_id канала (получить через @userinfobot или API после вступления бота в канал) + // Канал по ТЗ: https://t.me/+wHh_lW83AvVlYWNi + 'telegram_chat_id_dev' => getenv('TELEGRAM_ORDER_CONTROL_CHAT_ID_DEV') ?: '-1001861631125', + 'telegram_chat_id_prod' => getenv('TELEGRAM_ORDER_CONTROL_CHAT_ID_PROD') ?: '4886272326', + // Email получатели по ТЗ + 'email_recipients' => array_filter(explode(',', getenv('ORDER_CONTROL_EMAIL_RECIPIENTS') ?: 'vladimir.fomichev@erp-flowers.ru,ekaterina.geldak@bazacvetov24.ru,irina.rogacheva@bazacvetov24.ru,alena.chelyshkina@bazacvetov24.ru')), + 'email_subject' => '[Контроль MP] Отчёт о расхождениях статусов заказов', ], ]; diff --git a/erp24/config/web.php b/erp24/config/web.php index e58b6228..4cb53a6b 100644 --- a/erp24/config/web.php +++ b/erp24/config/web.php @@ -68,11 +68,21 @@ $config = [ 'errorHandler' => [ 'errorAction' => 'site/error', ], + // Mailer для отправки email 'mailer' => [ 'class' => \yii\symfonymailer\Mailer::class, 'viewPath' => '@app/mail', - // send all mails to a file by default. - 'useFileTransport' => true, + // В dev-режиме письма сохраняются в файл, в prod — отправляются через SMTP + 'useFileTransport' => getenv('APP_ENV') === 'development', + // SMTP-транспорт для production (настраивается через .env) + 'transport' => [ + 'scheme' => getenv('MAIL_SCHEME') ?: 'smtp', + 'host' => getenv('MAIL_HOST') ?: 'smtp.yandex.ru', + 'port' => (int)(getenv('MAIL_PORT') ?: 465), + 'username' => getenv('MAIL_USERNAME') ?: '', + 'password' => getenv('MAIL_PASSWORD') ?: '', + 'encryption' => getenv('MAIL_ENCRYPTION') ?: 'ssl', + ], ], 'log' => [ 'traceLevel' => 3, diff --git a/erp24/services/OrderControlReportService.php b/erp24/services/OrderControlReportService.php index 3adaa31e..7e702fed 100644 --- a/erp24/services/OrderControlReportService.php +++ b/erp24/services/OrderControlReportService.php @@ -191,17 +191,17 @@ class OrderControlReportService mo.total, mo.creation_date, mo.status_processing_1c as rmk_status_id, - mocs.name as rmk_status, + mocs.status as rmk_status, most.code as mp_status_code, mosub.code as mp_substatus_code, most.name as mp_status_name FROM marketplace_orders mo LEFT JOIN city_store cs ON cs.id = mo.store_id - LEFT JOIN marketplace_order_1c_statuses mocs ON mocs.id::text = mo.status_processing_1c + LEFT JOIN marketplace_order_1c_statuses mocs ON mocs.id = mo.status_processing_1c::integer LEFT JOIN marketplace_order_status_types most ON most.id = mo.status_id LEFT JOIN marketplace_order_status_types mosub ON mosub.id = mo.substatus_id WHERE mo.fake = 0 - AND mo.status_processing_1c IN (:rmk_1004, :rmk_1011) + AND mo.status_processing_1c::integer IN (:rmk_1004, :rmk_1011) AND mo.updated_at >= :start_date AND ( most.code IS NULL @@ -211,8 +211,8 @@ class OrderControlReportService "; $orders = Yii::$app->db->createCommand($sql, [ - ':rmk_1004' => '1004', - ':rmk_1011' => '1011', + ':rmk_1004' => 1004, + ':rmk_1011' => 1011, ':start_date' => $startDateStr, ':delivered' => MarketplaceOrderStatusTypes::DELIVERED_CODE, ':delivery_service_delivered' => MarketplaceOrderStatusTypes::DELIVERY_SERVICE_DELIVERED_CODE, @@ -359,13 +359,13 @@ class OrderControlReportService mo.total, mo.creation_date, mo.status_processing_1c as rmk_status_id, - mocs.name as rmk_status, + mocs.status as rmk_status, most.code as mp_status_code, mosub.code as mp_substatus_code, most.name as mp_status_name FROM marketplace_orders mo LEFT JOIN city_store cs ON cs.id = mo.store_id - LEFT JOIN marketplace_order_1c_statuses mocs ON mocs.id::text = mo.status_processing_1c + LEFT JOIN marketplace_order_1c_statuses mocs ON mocs.id = mo.status_processing_1c::integer LEFT JOIN marketplace_order_status_types most ON most.id = mo.status_id LEFT JOIN marketplace_order_status_types mosub ON mosub.id = mo.substatus_id WHERE mo.fake = 0 @@ -376,7 +376,7 @@ class OrderControlReportService ) AND ( mo.status_processing_1c IS NULL - OR mo.status_processing_1c NOT IN (:rmk_1005, :rmk_1012) + OR mo.status_processing_1c::integer NOT IN (:rmk_1005, :rmk_1012) ) ORDER BY cs.name ASC, mo.creation_date DESC "; @@ -385,8 +385,8 @@ class OrderControlReportService ':start_date' => $startDateStr, ':delivered' => MarketplaceOrderStatusTypes::DELIVERED_CODE, ':delivery_service_delivered' => MarketplaceOrderStatusTypes::DELIVERY_SERVICE_DELIVERED_CODE, - ':rmk_1005' => '1005', - ':rmk_1012' => '1012', + ':rmk_1005' => 1005, + ':rmk_1012' => 1012, ])->queryAll(); $issues = []; @@ -428,13 +428,13 @@ class OrderControlReportService mo.total, mo.creation_date, mo.status_processing_1c as rmk_status_id, - mocs.name as rmk_status, + mocs.status as rmk_status, most.code as mp_status_code, mosub.code as mp_substatus_code, most.name as mp_status_name FROM marketplace_orders mo LEFT JOIN city_store cs ON cs.id = mo.store_id - LEFT JOIN marketplace_order_1c_statuses mocs ON mocs.id::text = mo.status_processing_1c + LEFT JOIN marketplace_order_1c_statuses mocs ON mocs.id = mo.status_processing_1c::integer LEFT JOIN marketplace_order_status_types most ON most.id = mo.status_id LEFT JOIN marketplace_order_status_types mosub ON mosub.id = mo.substatus_id WHERE mo.fake = 0 @@ -442,7 +442,7 @@ class OrderControlReportService AND most.code = :cancelled AND ( mo.status_processing_1c IS NULL - OR mo.status_processing_1c NOT IN (:rmk_1006, :rmk_1013) + OR mo.status_processing_1c::integer NOT IN (:rmk_1006, :rmk_1013) ) ORDER BY cs.name ASC, mo.creation_date DESC "; @@ -450,8 +450,8 @@ class OrderControlReportService $orders = Yii::$app->db->createCommand($sql, [ ':start_date' => $startDateStr, ':cancelled' => MarketplaceOrderStatusTypes::CANSELLED_CODE, - ':rmk_1006' => '1006', - ':rmk_1013' => '1013', + ':rmk_1006' => 1006, + ':rmk_1013' => 1013, ])->queryAll(); $issues = []; @@ -855,25 +855,43 @@ class OrderControlReportService return false; } + // Диагностика конфигурации mailer + $this->logMailerDiagnostics($validRecipients); + $sent = false; + $lastError = null; $maxRetries = $this->config['max_retries'] ?? self::MAX_RETRIES; $retryDelay = $this->config['retry_delay_seconds'] ?? self::RETRY_DELAY_SECONDS; $subject = $this->config['email_subject'] ?? 'Контроль статусов заказов МП'; for ($attempt = 1; $attempt <= $maxRetries; $attempt++) { try { - $sent = Yii::$app->mailer->compose() + $message = Yii::$app->mailer->compose() ->setTo($validRecipients) ->setSubject($subject) - ->setHtmlBody($html) - ->send(); + ->setHtmlBody($html); + + // Устанавливаем отправителя, если настроен + $fromEmail = getenv('MAIL_USERNAME') ?: 'noreply@bazacvetov24.ru'; + $message->setFrom([$fromEmail => 'ERP24 Контроль МП']); + + $sent = $message->send(); if ($sent) { $this->logInfo('Email отправлен на: ' . implode(', ', $validRecipients)); break; + } else { + $lastError = 'Метод send() вернул false без исключения'; + $this->logWarning("Email попытка {$attempt}/{$maxRetries}: {$lastError}"); } } catch (\Exception $e) { - $this->logWarning("Email попытка {$attempt}/{$maxRetries}: {$e->getMessage()}"); + $lastError = $e->getMessage(); + $this->logError("Email попытка {$attempt}/{$maxRetries}", [ + 'error' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile() . ':' . $e->getLine(), + 'trace' => array_slice(explode("\n", $e->getTraceAsString()), 0, 5), + ]); } if ($attempt < $maxRetries) { @@ -882,12 +900,52 @@ class OrderControlReportService } if (!$sent) { - $this->logError('Не удалось отправить email после ' . $maxRetries . ' попыток'); + $this->logError('Не удалось отправить email после ' . $maxRetries . ' попыток', [ + 'last_error' => $lastError, + 'recipients' => $validRecipients, + ]); } return $sent; } + /** + * Логирует диагностику конфигурации mailer + * + * @param array $recipients Получатели + */ + private function logMailerDiagnostics(array $recipients): void + { + $mailer = Yii::$app->mailer; + + $diagnostics = [ + 'mailer_class' => get_class($mailer), + 'use_file_transport' => $mailer->useFileTransport ?? 'не определено', + 'recipients' => $recipients, + 'env' => [ + 'YII_ENV' => YII_ENV, + 'YII_ENV_DEV' => YII_ENV_DEV ? 'true' : 'false', + 'MAIL_HOST' => getenv('MAIL_HOST') ?: '(не задан)', + 'MAIL_PORT' => getenv('MAIL_PORT') ?: '(не задан)', + 'MAIL_USERNAME' => getenv('MAIL_USERNAME') ? '***настроен***' : '(не задан)', + 'MAIL_PASSWORD' => getenv('MAIL_PASSWORD') ? '***настроен***' : '(не задан)', + 'MAIL_ENCRYPTION' => getenv('MAIL_ENCRYPTION') ?: '(не задан)', + ], + ]; + + // Получаем конфигурацию транспорта, если доступна + if (method_exists($mailer, 'getTransport')) { + try { + $transport = $mailer->getTransport(); + $diagnostics['transport_class'] = get_class($transport); + } catch (\Exception $e) { + $diagnostics['transport_error'] = $e->getMessage(); + } + } + + $this->logInfo('Email диагностика mailer', $diagnostics); + } + /** * Разбивает длинное сообщение на части для Telegram * diff --git a/erp24/services/dto/OrderIssue.php b/erp24/services/dto/OrderIssue.php index c4b57e40..9a732f8c 100644 --- a/erp24/services/dto/OrderIssue.php +++ b/erp24/services/dto/OrderIssue.php @@ -169,7 +169,7 @@ class OrderIssue ); $issue->rmkStatus = $orderData['rmk_status'] ?? null; - $issue->rmkStatusId = $orderData['rmk_status_id'] ?? null; + $issue->rmkStatusId = isset($orderData['rmk_status_id']) ? (string)$orderData['rmk_status_id'] : null; $issue->mpStatus = $orderData['mp_status_name'] ?? null; $issue->mpStatusCode = $orderData['mp_status_code'] ?? null; $issue->mpSubstatusCode = $orderData['mp_substatus_code'] ?? null;