'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] Отчёт о расхождениях статусов заказов',
],
];
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
";
$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,
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 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
";
':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 = [];
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 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
";
$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 = [];
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) {
}
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
*