Загрузка данных


Проверять нужно **согласованность источников**: ответы МТС → запись сертификата → отчёт → отправка. Пустое ФИО в отчёте ещё не означает пустой сертификат.

Ниже запросы на чтение для **`myNewVasya`**, поля сверены с вашей выгрузкой схемы. Мероприятие 16518 отдельно не анализируем; технические ошибки его записей при наличии остаются в проверке.

**1. Сертификат есть, ФИО отчёта пустое**

Этот запрос покажет, сохранено ли ФИО в самой записи сертификата, а также ошибки привязки отчёта.

```sql
USE myNewVasya;

SELECT
    c.kod AS certificateRow,
    c.kodMerop,
    c.kodMember,
    c.kodReport,
    c.sessionId,
    c.participantId,
    c.mtsCertNumber,
    r.manFIO AS reportFIO,
    c.manFIO AS certificateFIO,
    c.field1Title,
    c.field1Value,
    c.field2Title,
    c.field2Value,
    c.formReceived,
    r.correctionApplied,
    r.processingStatus,
    c.firstSeenAt,
    c.lastCheckAt,
    c.lastError,
    CASE
        WHEN r.kod IS NULL
            THEN N'Отчёт не найден по kodReport'
        WHEN r.kodMember <> c.kodMember
            THEN N'Отчёт принадлежит другому участнику'
        WHEN NULLIF(LTRIM(RTRIM(c.manFIO)), N'') IS NOT NULL
            THEN N'ФИО в сертификате сохранено, в отчёте пусто'
        ELSE N'Проверить поля сертификата и исходный ответ API'
    END AS finding
FROM dbo.dat_mts_certificates AS c
LEFT JOIN dbo.sb_reports AS r ON r.kod = c.kodReport
WHERE NULLIF(LTRIM(RTRIM(c.mtsCertNumber)), N'') IS NOT NULL
  AND (
      r.kod IS NULL
      OR r.kodMember <> c.kodMember
      OR NULLIF(LTRIM(RTRIM(r.manFIO)), N'') IS NULL
  )
ORDER BY c.kodMerop, c.kodMember;
```

Запрос охватывает **всю таблицу**, поэтому число строк может отличаться от 136 в аудите за ограниченный период.

Разбор результата:

| Результат                                                              | Что проверять                                                                                          |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `certificateFIO` заполнено, `reportFIO` пустое                         | Перенос ФИО из сертификата в отчёт: условия обновления, статус отчёта, обработку корректировок         |
| `certificateFIO` пустое, но ФИО есть в `field1Value` или `field2Value` | Сопоставление названий полей в демоне. Ориентироваться на `field1Title`/`field2Title`, а не номер поля |
| Все поля ФИО пустые                                                    | Исходную выгрузку API и сам сертификат по персональной ссылке                                          |
| Отчёт отсутствует или принадлежит другому участнику                    | Привязку по мероприятию и участнику; такую запись нельзя исправлять простым копированием ФИО           |

**Не стоит сразу массово обновлять `sb_reports.manFIO`:** сначала нужно исключить неправильную привязку и перезапись уже применённых пользовательских исправлений.

**2. Сертификат без признака формы и форма без оценок**

```sql
SELECT
    c.kod AS certificateRow,
    c.kodMerop,
    c.kodMember,
    c.kodReport,
    c.sessionId,
    c.participantId,
    c.mtsCertNumber,
    c.formReceived,
    CASE WHEN NULLIF(LTRIM(RTRIM(c.formData)), N'') IS NULL
         THEN 0 ELSE 1 END AS hasStoredFormData,
    sc.kod AS scoreRow,
    sc.scoreValue,
    sc.scoreLector,
    sc.scoreInteractive,
    sc.scoreNPS,
    c.fallbackAt,
    c.internalSentAt,
    r.correctionApplied,
    r.processingStatus,
    c.firstSeenAt,
    c.lastCheckAt,
    c.updatedAt,
    c.lastError,
    CASE
        WHEN c.formReceived = 0
            THEN N'Номер сертификата есть, formReceived=0'
        WHEN sc.kod IS NULL
            THEN N'formReceived=1, записи оценок нет'
        ELSE N'Запись оценок есть, все числовые оценки NULL'
    END AS finding
FROM dbo.dat_mts_certificates AS c
LEFT JOIN dbo.sb_reports AS r ON r.kod = c.kodReport
LEFT JOIN dbo.dat_mts_scores AS sc
    ON sc.kodMerop = c.kodMerop
   AND sc.kodMember = c.kodMember
WHERE
    (
        NULLIF(LTRIM(RTRIM(c.mtsCertNumber)), N'') IS NOT NULL
        AND c.formReceived = 0
    )
    OR
    (
        c.formReceived = 1
        AND (
            sc.kod IS NULL
            OR (
                sc.scoreValue IS NULL
                AND sc.scoreLector IS NULL
                AND sc.scoreInteractive IS NULL
                AND sc.scoreNPS IS NULL
                AND sc.scoreQuality IS NULL
                AND sc.scoreTopics IS NULL
            )
        )
    )
ORDER BY c.kodMerop, c.kodMember;
```

Это **кандидаты для проверки**, а не точное воспроизведение проверки аудита: здесь показан непосредственно `formReceived`, без объединения с `errApplied`.

Для конкретной записи получите полный сохранённый ответ:

```sql
SELECT kod, kodMerop, kodMember, formData
FROM dbo.dat_mts_certificates
WHERE kod = 123; -- certificateRow из результата выше
```

Дальше сравните его со свежим экспортом постформы МТС:

* **В API есть ответы, в `formData` нет:** проблема загрузки или сопоставления участника.
* **Ответы сохранены, `formReceived=0`:** проверить условие установки признака и результат SQL-записи.
* **Оценки присутствуют в ответах, но отсутствуют в таблице:** проверить названия вопросов, разбор чисел и предупреждения `POST_FIELD_SKIP`.
* **В исходных ответах оценок нет:** проверить обязательность вопросов именно этой версии формы.
* **Сертификат есть, а постформа API пуста:** зафиксировать оба ответа API для одного `sessionId` и участника; не ставить признак формы автоматически только по наличию сертификата.

`lastError=NULL` не доказывает отсутствие ошибок: часть отказов обработки видна только в логе демона.

**3. Проверка 55 отсутствующих ссылок мероприятия 16028**

```sql
SELECT
    r.kod AS kodReport,
    m.kod AS kodMember,
    a.kod AS kodMerop,
    a.sessionId,
    m.participantId,
    m.profileId,
    m.beInOn,
    m.timeInMerop,
    r.a1,
    r.processingStatus,
    r.mtsFormLink,
    CASE
        WHEN a.sessionId IS NULL
          OR NULLIF(LTRIM(RTRIM(m.participantId)), N'') IS NULL
            THEN N'Нет идентификаторов для API'
        WHEN r.a1 IS NULL OR r.a1 NOT LIKE 'otzyvMTS[_]%'
            THEN N'Не подходит маршрут a1'
        WHEN ISNULL(r.processingStatus, '') IN
             ('ERR','PRC','DUP PRC','ERR PRC','EDT PRC')
            THEN N'Исключён текущим статусом'
        WHEN EXISTS (
            SELECT 1
            FROM dbo.dat_mts_certificates AS c
            WHERE c.kodReport = r.kod
              AND c.kodMember = m.kod
              AND (
                  c.fallbackAt IS NOT NULL
                  OR c.internalSentAt IS NOT NULL
              )
        )
            THEN N'Исключён внутренней обработкой сертификата'
        ELSE N'Кандидат get-link; проверить дату настройки и лог'
    END AS finding
FROM dbo.sb_reports AS r
JOIN dbo.dat_members AS m ON m.kod = r.kodMember
JOIN dbo.dat_activity AS a ON a.kod = m.kodMerop
WHERE a.kod = 16028
  AND NULLIF(LTRIM(RTRIM(r.mtsFormLink)), N'') IS NULL
ORDER BY r.kod;
```

После полного цикла 0.15.11 повторите запрос. Для оставшихся кандидатов найдите:

```text
MTS_LINK_FAILED report=...
CERT_LINK_REQUEST_FAILED
CERT_LINK_EMPTY
MTS_LINK_SUMMARY
```

Сопоставляйте **одновременно `sessionId` и `participationId` из body**. Если API вернул ссылку, а в SQL она не появилась — проверять UPDATE и его защитные условия. Если цикл прерван или запрос завершился SSL-ошибкой — это ещё не ошибка данных участника.

**4. Письмо участнику 10632747**

Сначала проверить непосредственно отправления — письмо может не относиться к журналу нового демона:

```sql
SELECT
    s.kod AS sendKod,
    s.kodMerop,
    s.kodMember,
    s.kodReport,
    s.tipFL,
    s.toEmail,
    s.sendEM,
    s.pause,
    s.srvsDT,
    s.dtSend,
    s.resultSend,
    s.g_UID,
    j.kod AS journalKod,
    j.kind,
    j.state,
    j.createdAt AS journalCreatedAt,
    j.appliedAt,
    j.sentAt
FROM dbo.sb_send AS s
LEFT JOIN dbo.mts_daemon_mail_v12 AS j ON j.sendKod = s.kod
WHERE s.kodMember = 10632747
  AND s.kodMerop = 15999
ORDER BY s.kod DESC;

-- Дополнительно: записи журнала без созданного отправления.
SELECT *
FROM dbo.mts_daemon_mail_v12
WHERE kodMember = 10632747
ORDER BY createdAt DESC;
```

* `pause=1` — выяснить причину приостановки.
* `sendEM=0` — проверить `resultSend` и журнал почтового обработчика.
* В `sb_send` отправка отмечена, а журнал остаётся `QUEUED` — проверить `WR_mtsReconcileMail_v13`.
* `sendKod` заполнен, но соответствующей строки `sb_send` нет — проверить целостность связи.
* Не сбрасывать статусы для повторной отправки до выяснения результата: можно отправить дубликат.

Остальные находки стоит разбирать после этих четырёх проверок. **Несоответствие формата и числа слушателей** сравнивайте с исходными ответами и правилами группового/индивидуального участия. **Повторный email в мероприятии 15732** сначала сопоставьте с `participantId` и `profileId`: один email сам по себе не основание объединять участников.

Для следующего разбора достаточно результатов этих запросов и `formData` нескольких показательных случаев — по одному на каждую причину. Полную выгрузку всех участников повторно делать не нужно.