From c3d9e521d269172a3326182f7e993c8f4c633eb1 Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Wed, 18 Feb 2026 11:48:12 +0300 Subject: [PATCH] =?utf8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D1=8F=D0=B5=D0=BC?= =?utf8?q?=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83=20=D0=BE=D0=B1=D1=80?= =?utf8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8=20=D0=BF=D0=B8=D1=81=D0=B5?= =?utf8?q?=D0=BC=20=D0=BE=D1=82=20Flowwow?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/commands/MarketplaceController.php | 22 +++ .../MarketplaceFlowwowEmailsController.php | 1 + erp24/media/controllers/FlowwowController.php | 10 +- ...001_improve_marketplace_flowwow_emails.php | 64 +++++++++ erp24/records/MarketplaceFlowwowEmails.php | 126 +++++++++++++++-- .../MarketplaceFlowwowEmailsSearch.php | 15 +- erp24/services/MarketplaceService.php | 130 ++++++++++++++++-- .../marketplace-flowwow-emails/index.php | 64 +++++++-- 8 files changed, 390 insertions(+), 42 deletions(-) create mode 100644 erp24/migrations/m260218_000001_improve_marketplace_flowwow_emails.php diff --git a/erp24/commands/MarketplaceController.php b/erp24/commands/MarketplaceController.php index 4c242fd0..d370164d 100644 --- a/erp24/commands/MarketplaceController.php +++ b/erp24/commands/MarketplaceController.php @@ -159,6 +159,28 @@ class MarketplaceController extends Controller return ExitCode::OK; } + /** + * Повторная обработка писем Flowwow, которые не были успешно обработаны. + * Выбирает из БД письма со статусом NEW или RETRY и запускает processMessage для каждого. + * + * Использование: php yii marketplace/retry-flowwow-emails + */ + public function actionRetryFlowwowEmails(): int + { + $progressCallback = function (string $message) { + $this->stdout($message . "\n", BaseConsole::FG_YELLOW); + }; + + $result = MarketplaceService::processUnprocessedEmails($progressCallback); + + $this->stdout( + "Итог: обработано {$result['processed']} из {$result['total']}, ошибок: {$result['failed']}.\n", + BaseConsole::FG_GREEN + ); + + return ExitCode::OK; + } + public function actionGetYandexOrders() { $fromDate = date('d-m-Y', strtotime('-1 day')); diff --git a/erp24/controllers/MarketplaceFlowwowEmailsController.php b/erp24/controllers/MarketplaceFlowwowEmailsController.php index c30c9fbb..3f382e13 100644 --- a/erp24/controllers/MarketplaceFlowwowEmailsController.php +++ b/erp24/controllers/MarketplaceFlowwowEmailsController.php @@ -42,6 +42,7 @@ class MarketplaceFlowwowEmailsController extends Controller { $searchModel = new MarketplaceFlowwowEmailsSearch(); $dataProvider = $searchModel->search($this->request->queryParams); + $dataProvider->query->with(['order']); return $this->render('index', [ 'searchModel' => $searchModel, diff --git a/erp24/media/controllers/FlowwowController.php b/erp24/media/controllers/FlowwowController.php index 04b0d076..7b1fa85c 100644 --- a/erp24/media/controllers/FlowwowController.php +++ b/erp24/media/controllers/FlowwowController.php @@ -70,15 +70,13 @@ class FlowwowController extends Controller $oldMail = 0; $seen = 0; - $countMessages = 0; - $count = 0; - $messages = MarketplaceService::getFlowwowOrdersFromMail($date, $since, $oldMail, null, $seen); - $countMessages = count($messages); - $count = MarketplaceService::processMessages($messages); + if (!is_array($messages)) { + return ['success' => false, 'processed' => 0, 'all' => 0]; + } - return ['success' => true, 'count' => $count]; + return ['success' => true, 'processed' => $messages['processed'], 'all' => $messages['all']]; } diff --git a/erp24/migrations/m260218_000001_improve_marketplace_flowwow_emails.php b/erp24/migrations/m260218_000001_improve_marketplace_flowwow_emails.php new file mode 100644 index 00000000..f9be76bc --- /dev/null +++ b/erp24/migrations/m260218_000001_improve_marketplace_flowwow_emails.php @@ -0,0 +1,64 @@ +addColumn( + 'marketplace_flowwow_emails', + 'subject_type', + $this->smallInteger()->null() + ->comment('Тип письма (1=NEW, 2=APPROVED, 3=CHANGED, 4=CANCELLED, 5=DELIVERED)') + ); + + $this->addColumn( + 'marketplace_flowwow_emails', + 'processing_attempts', + $this->integer()->notNull()->defaultValue(0) + ->comment('Счётчик попыток обработки') + ); + + $this->addColumn( + 'marketplace_flowwow_emails', + 'processed_at', + $this->timestamp()->null() + ->comment('Время успешной обработки') + ); + + $this->addColumn( + 'marketplace_flowwow_emails', + 'error_message', + $this->text()->null() + ->comment('Текст последней ошибки обработки') + ); + + $this->addColumn( + 'marketplace_flowwow_emails', + 'marketplace_order_id', + $this->string(50)->null() + ->comment('ID заказа Flowwow (связка с marketplace_orders.marketplace_order_id)') + ); + + // Все существующие записи помечаем как обработанные, чтобы новый механизм retry их не трогал. + // processed_at берётся из created_at — так дата обработки соответствует фактическому времени письма. + $this->execute( + "UPDATE marketplace_flowwow_emails SET email_status = 1, processed_at = COALESCE(created_at, NOW()) WHERE email_status IS NULL OR email_status = 0" + ); + } + + public function safeDown() + { + $this->dropColumn('marketplace_flowwow_emails', 'marketplace_order_id'); + $this->dropColumn('marketplace_flowwow_emails', 'error_message'); + $this->dropColumn('marketplace_flowwow_emails', 'processed_at'); + $this->dropColumn('marketplace_flowwow_emails', 'processing_attempts'); + $this->dropColumn('marketplace_flowwow_emails', 'subject_type'); + } +} diff --git a/erp24/records/MarketplaceFlowwowEmails.php b/erp24/records/MarketplaceFlowwowEmails.php index 42f33a22..7cb00e95 100644 --- a/erp24/records/MarketplaceFlowwowEmails.php +++ b/erp24/records/MarketplaceFlowwowEmails.php @@ -9,16 +9,28 @@ use Yii; * * @property int $id ID * @property string $subject Тема письма - * @property int|null $email_status Статус письма - разобрано - 1, не разобрано - 2 + * @property int|null $email_status Статус обработки * @property string $from Отправитель письма * @property string $to Получатель письма * @property string $date Дата письма * @property string $body Тело письма * @property string|null $created_at Дата создания записи + * @property int|null $subject_type Тип письма (1=NEW, 2=APPROVED, 3=CHANGED, 4=CANCELLED, 5=DELIVERED) + * @property int $processing_attempts Счётчик попыток обработки + * @property string|null $processed_at Время успешной обработки + * @property string|null $error_message Текст последней ошибки + * @property string|null $marketplace_order_id ID заказа Flowwow + * + * @property-read \yii_app\records\MarketplaceOrders|null $order Связанный заказ */ class MarketplaceFlowwowEmails extends \yii\db\ActiveRecord { + public const STATUS_NEW = 0; // Зарегистрировано, ожидает обработки + public const STATUS_PROCESSED = 1; // Успешно обработано + public const STATUS_ERROR = 2; // Ошибка (исчерпаны попытки) + public const STATUS_RETRY = 3; // Ожидает повторной обработки + public const MAX_PROCESSING_ATTEMPTS = 5; /** * {@inheritdoc} @@ -34,12 +46,13 @@ class MarketplaceFlowwowEmails extends \yii\db\ActiveRecord public function rules() { return [ - [['email_status'], 'default', 'value' => 0], + [['email_status'], 'default', 'value' => self::STATUS_NEW], [['subject', 'from', 'to', 'date', 'body'], 'required'], - [['date', 'created_at'], 'safe'], - [['body'], 'string'], - [['email_status'], 'integer'], - [['subject', 'from', 'to'], 'string', 'max' => 255], + [['date', 'created_at', 'processed_at'], 'safe'], + [['body', 'error_message'], 'string'], + [['email_status', 'subject_type', 'processing_attempts'], 'integer'], + [['subject', 'from', 'to'], 'string', 'max' => 255], + [['marketplace_order_id'], 'string', 'max' => 50], ]; } @@ -51,13 +64,106 @@ class MarketplaceFlowwowEmails extends \yii\db\ActiveRecord return [ 'id' => 'ID', 'subject' => 'Тема письма', - 'email_status' => 'Статус письма', - 'from' => 'Отправитель письма', - 'to' => 'Получатель письма', + 'email_status' => 'Статус', + 'from' => 'Отправитель', + 'to' => 'Получатель', 'date' => 'Дата письма', 'body' => 'Тело письма', - 'created_at' => 'Дата создания записи', + 'created_at' => 'Дата создания', + 'subject_type' => 'Тип письма', + 'processing_attempts' => 'Попыток', + 'processed_at' => 'Обработано в', + 'error_message' => 'Текст ошибки', + 'marketplace_order_id' => 'ID заказа', + ]; + } + + /** + * Связь с заказом маркетплейса по marketplace_order_id. + * Поле marketplace_order_id в emails хранит ID заказа из маркетплейса (например "123456"), + * которому соответствует marketplace_orders.marketplace_order_id. + */ + public function getOrder(): \yii\db\ActiveQuery + { + return $this->hasOne(MarketplaceOrders::class, ['marketplace_order_id' => 'marketplace_order_id']) + ->andWhere(['marketplace_id' => \yii_app\records\MarketplaceStore::FLOWWOW_WAREHOUSE_ID]); + } + + /** + * Помечает письмо как успешно обработанное. + * Устанавливает STATUS_PROCESSED и фиксирует время обработки. + */ + public function markAsProcessed(): bool + { + $this->email_status = self::STATUS_PROCESSED; + $this->processed_at = date('Y-m-d H:i:s'); + return $this->save(false, ['email_status', 'processed_at']); + } + + /** + * Помечает письмо как завершённое с ошибкой (исчерпаны попытки). + */ + public function markAsError(string $errorMessage): bool + { + $this->email_status = self::STATUS_ERROR; + $this->error_message = $errorMessage; + $this->processing_attempts++; + return $this->save(false, ['email_status', 'error_message', 'processing_attempts']); + } + + /** + * Помечает письмо для повторной обработки. + */ + public function markForRetry(string $reason): bool + { + $this->email_status = self::STATUS_RETRY; + $this->error_message = $reason; + $this->processing_attempts++; + return $this->save(false, ['email_status', 'error_message', 'processing_attempts']); + } + + /** + * Проверяет, разрешена ли ещё повторная обработка. + */ + public function isRetryAllowed(): bool + { + return $this->processing_attempts < self::MAX_PROCESSING_ATTEMPTS; + } + + /** + * Возвращает ActiveQuery для писем, ожидающих обработки (STATUS_NEW или STATUS_RETRY). + */ + public static function findUnprocessed(): \yii\db\ActiveQuery + { + return static::find() + ->where(['in', 'email_status', [self::STATUS_NEW, self::STATUS_RETRY]]) + ->andWhere(['<', 'processing_attempts', self::MAX_PROCESSING_ATTEMPTS]); + } + + /** + * Текстовые метки статусов обработки. + */ + public static function statusLabels(): array + { + return [ + self::STATUS_NEW => 'Необработано', + self::STATUS_PROCESSED => 'Обработано', + self::STATUS_ERROR => 'Ошибка', + self::STATUS_RETRY => 'Повтор', ]; } + /** + * Текстовые метки типов писем. + */ + public static function subjectTypeLabels(): array + { + return [ + 1 => 'Новый заказ', + 2 => 'Принят', + 3 => 'Изменён', + 4 => 'Отменён', + 5 => 'Доставлен', + ]; + } } diff --git a/erp24/records/MarketplaceFlowwowEmailsSearch.php b/erp24/records/MarketplaceFlowwowEmailsSearch.php index 13f2e814..08deeb14 100644 --- a/erp24/records/MarketplaceFlowwowEmailsSearch.php +++ b/erp24/records/MarketplaceFlowwowEmailsSearch.php @@ -17,8 +17,9 @@ class MarketplaceFlowwowEmailsSearch extends MarketplaceFlowwowEmails public function rules() { return [ - [['id', 'email_status'], 'integer'], - [['subject', 'from', 'to', 'date', 'body', 'created_at', 'email_status'], 'safe'], + [['id', 'email_status', 'subject_type', 'processing_attempts'], 'integer'], + [['subject', 'from', 'to', 'date', 'body', 'created_at', 'email_status', + 'processed_at', 'error_message', 'marketplace_order_id'], 'safe'], ]; } @@ -43,8 +44,6 @@ class MarketplaceFlowwowEmailsSearch extends MarketplaceFlowwowEmails { $query = MarketplaceFlowwowEmails::find(); - // add conditions that should always apply here - $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); @@ -52,8 +51,6 @@ class MarketplaceFlowwowEmailsSearch extends MarketplaceFlowwowEmails $this->load($params, $formName); if (!$this->validate()) { - // uncomment the following line if you do not want to return any records when validation fails - // $query->where('0=1'); return $dataProvider; } @@ -62,13 +59,17 @@ class MarketplaceFlowwowEmailsSearch extends MarketplaceFlowwowEmails 'id' => $this->id, 'date' => $this->date, 'created_at' => $this->created_at, + 'subject_type' => $this->subject_type, + 'processing_attempts' => $this->processing_attempts, ]); $query->andFilterWhere(['ilike', 'subject', $this->subject]) ->andFilterWhere(['email_status' => $this->email_status]) ->andFilterWhere(['ilike', 'from', $this->from]) ->andFilterWhere(['ilike', 'to', $this->to]) - ->andFilterWhere(['ilike', 'body', $this->body]); + ->andFilterWhere(['ilike', 'body', $this->body]) + ->andFilterWhere(['ilike', 'error_message', $this->error_message]) + ->andFilterWhere(['ilike', 'marketplace_order_id', $this->marketplace_order_id]); return $dataProvider; } diff --git a/erp24/services/MarketplaceService.php b/erp24/services/MarketplaceService.php index 2f9bae88..327eabf8 100644 --- a/erp24/services/MarketplaceService.php +++ b/erp24/services/MarketplaceService.php @@ -2197,13 +2197,24 @@ class MarketplaceService } $savedEmail = self::saveEmailIfNotExists($subject, null, $from, $to, $date, $htmlMessage); + // Если письмо уже существует — подгружаем из БД + if ($savedEmail === null) { + $emailRecord = MarketplaceFlowwowEmails::find() + ->where(['subject' => $subject, 'from' => $from, 'date' => $date]) + ->one(); + } else { + $emailRecord = $savedEmail; + } + + // Уже обработанное письмо — только ставим SEEN и пропускаем + if ($emailRecord !== null && $emailRecord->email_status === MarketplaceFlowwowEmails::STATUS_PROCESSED) { + imap_setflag_full($inbox, $email_number, "\\Seen"); + continue; + } + foreach ($subjectPatterns as $pattern) { if (preg_match($pattern, $subject)) { $subjectIndex = self::SUBJECT_INDEX[$pattern]; - if ($savedEmail !== null) { - $savedEmail->email_status = 1; - $savedEmail->save(); - } $message = [ 'subject' => $subject, 'subject_index' => $subjectIndex, @@ -2213,9 +2224,17 @@ class MarketplaceService 'body' => quoted_printable_decode($htmlMessage), ]; - $output = MarketplaceService::processMessage($message); + try { + $output = MarketplaceService::processMessage($message); + + if ($emailRecord !== null) { + $orderData = self::getOrdersDataFromMessage($message); + if (!empty($orderData)) { + $emailRecord->marketplace_order_id = (string)key($orderData); + } + $emailRecord->markAsProcessed(); + } - if ($output > 0) { self::imap_debug_log("Установка флага SEEN для сообшения #" . $email_number, $debugMode, $progressCallback); $result = imap_setflag_full($inbox, $email_number, "\\Seen"); if (!$result) { @@ -2231,8 +2250,18 @@ class MarketplaceService } else { self::imap_debug_log("WARNING: Сообщение #" . $email_number . " не удалось пометить как SEEN", $debugMode, $progressCallback); } + + $countProcessedMessages += $output; + } catch (\Throwable $e) { + Yii::error('Ошибка при обработке письма: ' . $e->getMessage(), __METHOD__); + if ($emailRecord !== null) { + if ($emailRecord->isRetryAllowed()) { + $emailRecord->markForRetry($e->getMessage()); + } else { + $emailRecord->markAsError($e->getMessage()); + } + } } - $countProcessedMessages += $output; if ($progressCallback) { call_user_func($progressCallback, "От: " . $from . " тема " . $subject . " от " . $date); @@ -2257,6 +2286,85 @@ class MarketplaceService return ['processed' => $countProcessedMessages, 'all' => $countAllMessages]; } + /** + * Определяет тип письма по теме через SUBJECT_INDEX regex patterns. + * + * @param string $subject Тема письма (уже декодированная) + * @return int|null Тип письма (1=NEW, 2=APPROVED, 3=CHANGED, 4=CANCELLED, 5=DELIVERED) или null + */ + private static function detectSubjectType(string $subject): ?int + { + foreach (self::SUBJECT_INDEX as $pattern => $index) { + if (preg_match($pattern, $subject)) { + return $index; + } + } + return null; + } + + /** + * Обрабатывает все необработанные письма из БД (статус NEW или RETRY с заполненным subject_type). + * Используется командой marketplace/retry-flowwow-emails для повторной обработки. + * + * @param callable|null $progressCallback + * @return array ['processed' => int, 'failed' => int, 'total' => int] + */ + public static function processUnprocessedEmails(?callable $progressCallback = null): array + { + $emails = MarketplaceFlowwowEmails::findUnprocessed() + ->andWhere(['not', ['subject_type' => null]]) + ->all(); + + $total = count($emails); + $processed = 0; + $failed = 0; + + if ($progressCallback) { + call_user_func($progressCallback, "Найдено необработанных писем: {$total}"); + } + + foreach ($emails as $emailRecord) { + $message = [ + 'subject' => $emailRecord->subject, + 'subject_index' => $emailRecord->subject_type, + 'from' => $emailRecord->from, + 'to' => $emailRecord->to, + 'date' => $emailRecord->date, + 'body' => $emailRecord->body, + ]; + + try { + self::processMessage($message); + + $orderData = self::getOrdersDataFromMessage($message); + if (!empty($orderData)) { + $emailRecord->marketplace_order_id = (string)key($orderData); + } + $emailRecord->markAsProcessed(); + $processed++; + + if ($progressCallback) { + call_user_func($progressCallback, "Обработано письмо #{$emailRecord->id}: {$emailRecord->subject}"); + } + } catch (\Throwable $e) { + $failed++; + Yii::error("Ошибка при повторной обработке письма #{$emailRecord->id}: " . $e->getMessage(), __METHOD__); + + if ($emailRecord->isRetryAllowed()) { + $emailRecord->markForRetry($e->getMessage()); + } else { + $emailRecord->markAsError($e->getMessage()); + } + + if ($progressCallback) { + call_user_func($progressCallback, "Ошибка письма #{$emailRecord->id}: " . $e->getMessage()); + } + } + } + + return ['processed' => $processed, 'failed' => $failed, 'total' => $total]; + } + public static function saveEmailIfNotExists($subject, $subjectPattern, $from, $to, $date, $body) { if (strpos($from, 'info@flowwow.com') === false) { @@ -2280,6 +2388,7 @@ class MarketplaceService $email->date = $date; $email->body = quoted_printable_decode($body); $email->created_at = date('Y-m-d H:i:s'); + $email->subject_type = self::detectSubjectType($subject); if ($email->save()) { return $email; @@ -2687,6 +2796,7 @@ class MarketplaceService $statuses = ArrayHelper::map($statuses, 'code', 'id'); $statusCodes = array_unique(array_keys($statuses)); $newOrdersCount = 0; + $processingSuccess = false; $campaignId = $store; // Проверяем, что $order не пустой @@ -2717,6 +2827,7 @@ class MarketplaceService if ($index == self::SUBJECT_INDEX[self::SUBJECT_NEW]) { $marketplaceOrder = self::createOrderFlowwow($orderDetails, $campaignId, $statusId, $substatusId); if ($marketplaceOrder && $marketplaceOrder->save()) { + $processingSuccess = true; self::sendMessageToTelegram($marketplaceOrder->guid, "Новый заказ Флаувау №" . $marketplaceOrder->marketplace_order_id); $newOrdersCount += 1; self::createOrUpdateStatusHistory($marketplaceOrder->id, $statusId, $substatusId, $orderDetails); @@ -2747,6 +2858,7 @@ class MarketplaceService } if ($marketplaceOrder->save()) { + $processingSuccess = true; self::createOrUpdateStatusHistory($marketplaceOrder->id, $statusId, $substatusId, $orderDetails); if (isset($orderDetails['delivery'])) { $deliveryRecord = self::saveFromDeliveryText($marketplaceOrder->id, $orderDetails['delivery']); @@ -2781,6 +2893,7 @@ class MarketplaceService if ($isChanged) { $marketplaceOrder->raw_data = json_encode($oldRawData, JSON_UNESCAPED_UNICODE); if ($marketplaceOrder->save()) { + $processingSuccess = true; self::createOrUpdateStatusHistory($marketplaceOrder->id, $statusId, $substatusId, $orderDetails); if (isset($orderDetails['delivery'])) { $deliveryRecord = self::saveFromDeliveryText($marketplaceOrder->id, $orderDetails['delivery']); @@ -2796,6 +2909,7 @@ class MarketplaceService } else { // отмена или успешное выполнение if ($marketplaceOrder->save()) { + $processingSuccess = true; self::createOrUpdateStatusHistory($marketplaceOrder->id, $statusId, $substatusId, $orderDetails); } else { Yii::error('Не удалось обновить заказ' . json_encode($marketplaceOrder->getErrors(), JSON_UNESCAPED_UNICODE)); @@ -2805,7 +2919,7 @@ class MarketplaceService self::checkAndSetReadyTo1c($marketplaceOrder); } - return $newOrdersCount; + return $processingSuccess ? max($newOrdersCount, 1) : 0; } /** diff --git a/erp24/views/marketplace-flowwow-emails/index.php b/erp24/views/marketplace-flowwow-emails/index.php index d30df574..a71ea666 100644 --- a/erp24/views/marketplace-flowwow-emails/index.php +++ b/erp24/views/marketplace-flowwow-emails/index.php @@ -31,24 +31,66 @@ $this->params['breadcrumbs'][] = $this->title; 'id', 'subject', - + [ 'attribute' => 'email_status', 'format' => 'raw', 'value' => function ($model) { - $text = ''; - $class = ''; - if ($model->email_status === 1) { - $class = 'bg-success text-white'; - $text = 'Обработано'; - } elseif ($model->email_status === 0) { - $class = 'bg-danger text-white'; - $text = 'Необработано'; - } + $labels = [ + MarketplaceFlowwowEmails::STATUS_NEW => ['Необработано', 'bg-danger text-white'], + MarketplaceFlowwowEmails::STATUS_PROCESSED => ['Обработано', 'bg-success text-white'], + MarketplaceFlowwowEmails::STATUS_ERROR => ['Ошибка', 'bg-dark text-white'], + MarketplaceFlowwowEmails::STATUS_RETRY => ['Повтор', 'bg-primary text-white'], + ]; + [$text, $class] = $labels[$model->email_status] ?? ['—', '']; return Html::tag('span', $text, ['class' => "badge $class"]); }, - 'filter' => Html::input('text', 'MarketplaceFlowwowEmailsSearch[email_status]', $searchModel->email_status, ['class' => 'form-control']), + 'filter' => Html::activeDropDownList( + $searchModel, + 'email_status', + MarketplaceFlowwowEmails::statusLabels(), + ['class' => 'form-control', 'prompt' => '— все —'] + ), + ], + + [ + 'attribute' => 'subject_type', + 'value' => fn($model) => MarketplaceFlowwowEmails::subjectTypeLabels()[$model->subject_type] ?? '—', + 'filter' => Html::activeDropDownList( + $searchModel, + 'subject_type', + MarketplaceFlowwowEmails::subjectTypeLabels(), + ['class' => 'form-control', 'prompt' => '— все —'] + ), ], + + [ + 'attribute' => 'marketplace_order_id', + 'format' => 'raw', + 'value' => function ($model) { + if (!$model->marketplace_order_id) { + return '—'; + } + $order = $model->order; + if ($order) { + return Html::a( + '№' . $model->marketplace_order_id, + ['/marketplace-orders/view', 'id' => $order->id], + ['class' => 'btn btn-xs btn-outline-primary', 'target' => '_blank'] + ); + } + return Html::encode($model->marketplace_order_id) . ' (не найден)'; + }, + 'filter' => Html::activeInput( + 'text', + $searchModel, + 'marketplace_order_id', + ['class' => 'form-control', 'placeholder' => '№ заказа'] + ), + ], + + 'processing_attempts', + 'from', 'to', 'date', -- 2.39.5