6a2e9b7122
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
244 lines
39 KiB
HTML
244 lines
39 KiB
HTML
{{define "content"}}
|
||
<div class="doc-header">
|
||
<h1>bj ↔ bj2depo: трансферные таблицы FTV_M2M и события</h1>
|
||
<p class="lede">Для разработчика Fancy (сервис bj2depo). Как bj заполняет FTV_M2M / FTV_M2M_LINES, какими идентификаторами располагает, какие события пишет в FTV_M2M_EVENTS и какие ожидает в ответ. <strong>Согласовано 10.09.2026 и реализовано в bj:</strong> события BJ_* пишутся, читаются DEPO_ORDER_CREATED и DEPO_ERROR, справочник участников перезаписывается при каждом получении.</p>
|
||
</div>
|
||
|
||
<div class="doc">
|
||
<nav class="doc-toc" id="toc">
|
||
<div class="toc-group">Основа</div>
|
||
<a href="#roles">Кто что делает</a>
|
||
<a href="#ids">Идентификаторы у bj</a>
|
||
<a href="#tables">Таблицы</a>
|
||
<div class="toc-group">События</div>
|
||
<a href="#rules">Правила</a>
|
||
<a href="#bj-events">События от bj</a>
|
||
<a href="#depo-events">События от bj2depo</a>
|
||
<a href="#timeline">Порядок по этапам</a>
|
||
<a href="#actions">Действия джобы по событиям</a>
|
||
<a href="#handbook">Справочник участников</a>
|
||
<a href="#runmode">Кто запускает обработку</a>
|
||
<a href="#replay">Выгрузка истории тестов</a>
|
||
<div class="toc-group">Практика</div>
|
||
<a href="#sql">Примеры SQL</a>
|
||
<a href="#questions">Открытые вопросы</a>
|
||
</nav>
|
||
|
||
<article class="doc-body">
|
||
|
||
<h2 id="roles">Кто что делает</h2>
|
||
<table>
|
||
<thead><tr><th style="width:130px">Сервис</th><th>Роль</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><strong>bj</strong> (Bridge-and-Join-s)</td><td>обмен с НРД по MOEX МОСТ (M2MTR / M2MTD / M2MER, депо-поручения 16 и 16/1, отчёты MS010), сверка с DEPO/SPECTRE, блокировка и движение бумаг в QUIK МНП, личный кабинет. Пишет поручения и события в трансферные таблицы, читает события от DEPO.</td></tr>
|
||
<tr><td><strong>bj2depo</strong> (джоба Fancy)</td><td>читает FTV_M2M / FTV_M2M_LINES → создаёт и меняет сущности DEPO (M2M-поручения D_CLISER_M2M, строки D_CLISER_M2M_DOL); обрабатывает события FTV_M2M_EVENTS от bj; пишет свои события для bj.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="muted">Работаем только с НРД (MOEX МОСТ), СПБ нет. Переводятся только выпуски с ISIN, начинающимся на RU. База — наша копия DEPO на SQL002 (<code>BJ_DEPO_DATA</code>); джобу bj2depo нужно поднять на ней (сейчас она работает на SQL001/UPD_DE_DATA, и записи в нашей базе никто не забирает).</p>
|
||
|
||
<h2 id="ids">Какими идентификаторами располагает bj</h2>
|
||
<table>
|
||
<thead><tr><th style="width:190px">Сущность</th><th>Что есть у bj</th><th style="width:260px">Что можем передавать в таблицы</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Депонент</td><td>ФИО + серия/номер паспорта из сообщения НРД. bj сам находит лицо в DEPO по паспорту (серия без пробелов, <code>OD_FACES.DOC_SER/DOC_NUM</code>).</td><td><code>DEPONENT_NAME</code>, <code>DEPONENT_DOC</code> и <code>DEPONENT_ID = OD_FACES.SELF_ID</code> (мастер-ID лица; у дублей указывает на основную запись).</td></tr>
|
||
<tr><td>Контрагент (депозитарий)</td><td>код участника МОСТ (<code>MC…</code>, 12 знаков), депозитарный код НРД (<code>HL…</code>), ИНН, наименование из справочника участников M2M. В <code>OD_FACES</code> депозитариев-контрагентов нет (проверено по ИНН), есть только сам НКО АО НРД (ID 18174).</td><td><code>CONTRAGENT_M2M_CODE</code>; при желании ИНН и наименование — доп. поля.</td></tr>
|
||
<tr><td>Бумага</td><td>ISIN + код НРД (напр. <code>SBER/03</code>); bj находит <code>OD_VALUES.ID</code> по ISIN (<code>IS_IN=1</code> = принята на обслуживание).</td><td><code>ISIN</code>, <code>SEC_CODE</code> и <code>SHARE = OD_VALUES.ID</code>.</td></tr>
|
||
<tr><td>Счёт клиента</td><td>bj находит открытый торговый счёт <code>ВТНКЦ…</code> (<code>OD_CLIENTS</code>, <code>STATE=0</code>). При нескольких договорах берётся самый ранний (правило депозитария).</td><td><code>OD_CLIENTS.ID</code> и <code>NUMBER</code>.</td></tr>
|
||
<tr><td>Счёт депозитария (место хранения)</td><td>наш счёт в НРД <code>HL171004001C</code> + раздел <code>36MC04136…</code>. В DEPO это запись <code>OD_CLIENTS</code> с NUMBER <code>HL171004001C/253400M18U5TB02TW421/253400_X1A47/<раздел></code> (например ID 79 для раздела 36MC0413600000F00). <code>OD_CLIENTS_REC.LINK_NUMBER</code> у нас пустой везде.</td><td><code>PLACE_NUMBER</code> = раздел (или ID записи OD_CLIENTS, если удобнее).</td></tr>
|
||
<tr><td>Причины отказа</td><td>коды M2M01…M2M10 по каждой бумаге (может быть несколько), при отказе НРД по поручению 16/16-1 — код и текст из MS010.</td><td><code>M2M_REJECT_CODE</code> через <code>;</code>, текст — в <code>COMMENT</code>.</td></tr>
|
||
<tr><td>Ключи</td><td><code>GUID</code> перевода (из M2MTR) и <code>REFERENCE</code> строки (референс бумаги из M2MTR, <code>M2M…</code>).</td><td>как ключи FTV_M2M / FTV_M2M_LINES / FTV_M2M_EVENTS.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2 id="tables">Таблицы (как согласовано, поля, которые заполняет bj)</h2>
|
||
<table>
|
||
<thead><tr><th style="width:170px">Таблица</th><th>Поля от bj</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>FTV_M2M</code></td><td><code>GUID</code>, <code>DIRECTION</code> (IN — к нам зачисляют, OUT — мы списываем), <code>CONTRAGENT_M2M_CODE</code>, <code>DEPONENT_NAME</code>, <code>DEPONENT_DOC</code>, <code>DEPONENT_ID</code> (если нашли), <code>COMMENT</code>. Вставка вместе со строками в одной транзакции.</td></tr>
|
||
<tr><td><code>FTV_M2M_LINES</code></td><td><code>GUID</code>, <code>REFERENCE</code>, <code>ISIN</code>, <code>SEC_CODE</code>, <code>SHARE</code> (если нашли), <code>AMOUNT</code> (штуки, дробные для паёв), <code>PLACE_NUMBER</code>, <code>DEPO_NUMBER</code> — номер торгового счёта клиента <code>ВТНКЦ…</code> (джоба находит по нему <code>DEPO</code>).</td></tr>
|
||
<tr><td><code>FTV_M2M_EVENTS</code></td><td><code>GUID</code>, <code>REFERENCE</code> (пусто = по поручению целиком), <code>EVENT_TYPE</code>, <code>EVENT_DATE</code>, <code>M2M_REJECT_CODE</code>, <code>COMMENT</code>, <code>STATE</code> (см. правила).</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="muted">Поручение создаётся в DEPO на самом раннем этапе: сразу после записи в FTV_M2M (для входящих — как только пришёл M2MTR; для исходящих — как только наш M2MTR ушёл в НРД).</p>
|
||
|
||
<h2 id="rules">Правила для событий</h2>
|
||
<ul>
|
||
<li><strong>Одно событие — одна строка.</strong> Ключ идемпотентности: <code>GUID + REFERENCE + EVENT_TYPE</code>; повтор не пишется.</li>
|
||
<li><strong>Направление по префиксу типа:</strong> <code>BJ_*</code> пишет bj и читает bj2depo; <code>DEPO_*</code> пишет bj2depo и читает bj. Если удобнее, вместо префикса — колонка <code>SOURCE</code>.</li>
|
||
<li><strong><code>STATE</code> — состояние обработки получателем:</strong> пишущая сторона ставит <code>TODO</code>; получатель после обработки — <code>SUCCESS</code> или <code>ERROR</code>, в <code>COMMENT</code> — что сделано или почему не вышло; <code>TimeBeg/TimeEnd</code> — время обработки.</li>
|
||
<li><strong>Порядок:</strong> события одного GUID обрабатываются по <code>ID</code>. Событие по строке (<code>REFERENCE</code> задан) относится к одной бумаге; без <code>REFERENCE</code> — ко всему поручению.</li>
|
||
<li><strong>Коды отказа</strong> в <code>M2M_REJECT_CODE</code> через <code>;</code>, как их даёт НРД (M2M01…M2M10), человекочитаемый текст — в <code>COMMENT</code>.</li>
|
||
</ul>
|
||
|
||
<h2 id="bj-events">События от bj (обрабатывает bj2depo)</h2>
|
||
<table>
|
||
<thead><tr><th style="width:230px">EVENT_TYPE</th><th>Когда bj пишет</th><th>Уровень</th><th>Что ожидаем от bj2depo</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>BJ_RECEIVED</code></td><td>пришёл входящий M2MTR от другого депозитария (мы зачисляем); одновременно вставлены FTV_M2M + LINES</td><td>поручение</td><td>создать M2M-поручение в DEPO, вернуть <code>DEPO_ORDER_CREATED</code></td></tr>
|
||
<tr><td><code>BJ_SENT</code></td><td>наш исходящий M2MTR ушёл в НРД (мы списываем)</td><td>поручение</td><td>то же для списания</td></tr>
|
||
<tr><td><code>BJ_DECISION_CONFIRM</code> / <code>BJ_DECISION_REJECT</code></td><td>мы ответили контрагенту по бумаге (M2MTD); при отказе — коды</td><td>строка</td><td>проставить статус строки: принято / отказано с кодами</td></tr>
|
||
<tr><td><code>BJ_CP_CONFIRM</code> / <code>BJ_CP_REJECT</code></td><td>контрагент ответил по нашему исходящему (M2MTD)</td><td>строка</td><td>то же для списания</td></tr>
|
||
<tr><td><code>BJ_SERVICE_ERROR</code></td><td>сервис МОСТ отверг перевод (M2MER ERROR), код M2Mxx</td><td>поручение</td><td>закрыть поручение как отклонённое</td></tr>
|
||
<tr><td><code>BJ_ORDER_SENT</code></td><td>подано депо-поручение 16 (списание) или 16/1 (зачисление) в НРД; в COMMENT — CONTR_D_ID и дата</td><td>поручение</td><td>привязать номер поручения НРД</td></tr>
|
||
<tr><td><code>BJ_ORDER_ACCEPTED</code> / <code>BJ_ORDER_REJECTED</code></td><td>НРД принял (отчёт MS010) или отверг наше депо-поручение; при отказе — код и текст</td><td>поручение</td><td>проставить результат; при принятии — провести движение по счёту, если DEPO делает это по нашему сигналу (см. вопросы)</td></tr>
|
||
<tr><td><code>BJ_ORDER_LATE</code></td><td>MS010 не получен за 5 минут после подачи (регламент)</td><td>поручение</td><td>для информации / контроля</td></tr>
|
||
<tr><td><code>BJ_EXECUTED</code></td><td>перевод исполнен (по отчёту НРД)</td><td>поручение</td><td>закрыть как исполненное</td></tr>
|
||
<tr><td><code>BJ_CANCELLED</code></td><td>перевод отозван / аннулирован / истёк срок 30 дней без исполнения</td><td>поручение</td><td>закрыть как отменённое</td></tr>
|
||
<tr><td><code>BJ_GATEWAY_REJECT</code></td><td>шлюз bj отказал в исходящем переводе из ЛК до отправки в НРД (сверка: лицо, счёт, бумага, остаток, место хранения, блокировки; либо данные заявки некорректны). Код в M2M_REJECT_CODE (M2Mxx или QTY/PLACE/QUIK), причина в COMMENT; одновременно вставлены FTV_M2M (OUT) + LINES</td><td>строка</td><td>закрыть строку и документ как отклонённые (см. «Действия джобы»); <strong>пометить событие SUCCESS или ERROR</strong> — только после этого bj ставит заявке в ЛК «Отклонена»</td></tr>
|
||
<tr><td><code>BJ_PING</code></td><td>проверка готовности обработчика из настроек bj (GUID случайный, без шапки в FTV_M2M)</td><td>служебное</td><td><strong>ничего не делать, только пометить SUCCESS</strong> — bj ждёт отметку до 40 с и удаляет строку</td></tr>
|
||
<tr><td><code>BJ_QUIK_BLOCKED</code> / <code>BJ_QUIK_RELEASED</code> / <code>BJ_QUIK_SIGNED</code> / <code>BJ_QUIK_DEPOSITED</code></td><td>блокировка / снятие / списание / зачисление в QUIK МНП; номер поручения МНП в COMMENT</td><td>строка</td><td>только для сверки QUIK ↔ DEPO, в DEPO ничего не менять</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2 id="depo-events">События от bj2depo (обрабатывает bj)</h2>
|
||
<p><strong>Согласовано:</strong> bj2depo пишет только <code>DEPO_ORDER_CREATED</code> и <code>DEPO_ERROR</code>. Остальные типы из таблицы ниже (проверка, исполнение, отмена) остаются в резерве на случай, если понадобятся позже; bj их пока не ждёт.</p>
|
||
<table>
|
||
<thead><tr><th style="width:230px">EVENT_TYPE</th><th>Когда bj2depo пишет</th><th>Уровень</th><th>Что делает bj</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>DEPO_ORDER_CREATED</code></td><td>поручение в DEPO создано; номер документа в COMMENT (и/или <code>FANSY_DOC</code> в FTV_M2M)</td><td>поручение</td><td>показывает номер в карточке перевода, далее ссылается на него</td></tr>
|
||
<tr><td><code>DEPO_ERROR</code></td><td>джоба не смогла обработать событие или поручение (текст)</td><td>любой</td><td>оповещение оператору bj</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="muted">Согласовано с программистом Fancy: от bj2depo достаточно двух событий, <code>DEPO_ORDER_CREATED</code> и <code>DEPO_ERROR</code>. Проверку строк по регламенту делает bj (сверка с DEPO), состояние документа bj читает сам (раздел «Обратная связь в bj»), поэтому отдельные события о проверке, исполнении и отмене не нужны.</p>
|
||
|
||
<h2 id="timeline">Порядок событий по этапам</h2>
|
||
<table>
|
||
<thead><tr><th style="width:280px">Входящий перевод (мы зачисляем)</th><th>Исходящий перевод (мы списываем)</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>1. bj: FTV_M2M + LINES, <code>BJ_RECEIVED</code></td><td>1. bj: QUIK блокирует бумаги (<code>BJ_QUIK_BLOCKED</code>), M2MTR в НРД, FTV_M2M + LINES, <code>BJ_SENT</code></td></tr>
|
||
<tr><td>2. bj2depo: <code>DEPO_ORDER_CREATED</code>; bj сверяет строки с DEPO</td><td>2. bj2depo: <code>DEPO_ORDER_CREATED</code></td></tr>
|
||
<tr><td>3. bj: ответ контрагенту — <code>BJ_DECISION_CONFIRM</code> / <code>BJ_DECISION_REJECT</code></td><td>3. bj: ответ контрагента — <code>BJ_CP_CONFIRM</code> / <code>BJ_CP_REJECT</code> (при отказе QUIK снимает блокировку: <code>BJ_QUIK_RELEASED</code>)</td></tr>
|
||
<tr><td>4. bj: подача 16/1 — <code>BJ_ORDER_SENT</code></td><td>4. bj: подача 16 — <code>BJ_ORDER_SENT</code></td></tr>
|
||
<tr><td>5. bj: <code>BJ_ORDER_ACCEPTED</code> (MS010) или <code>BJ_ORDER_REJECTED</code>; QUIK зачисляет — <code>BJ_QUIK_DEPOSITED</code></td><td>5. bj: <code>BJ_ORDER_ACCEPTED</code> / <code>BJ_ORDER_REJECTED</code>; QUIK списывает — <code>BJ_QUIK_SIGNED</code></td></tr>
|
||
<tr><td>6. bj2depo: движение по счёту депо по <code>BJ_ORDER_ACCEPTED</code> / <code>BJ_EXECUTED</code>, документ в «Конец обработки»</td><td>6. bj2depo: то же, списание</td></tr>
|
||
<tr><td colspan="2">Отклонения: <code>BJ_GATEWAY_REJECT</code> (шлюз bj отказал до отправки в НРД; статус в ЛК — после обработки джобой), <code>BJ_SERVICE_ERROR</code>, <code>BJ_ORDER_LATE</code> (5 мин без MS010), <code>BJ_CANCELLED</code> (30 дней без исполнения), <code>DEPO_ERROR</code>.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
|
||
<h2 id="actions">Действия джобы по событиям: что делать с документом DEPO</h2>
|
||
<p>Сейчас джоба создаёт документ по шапке и помечает события обработанными, но документ и его строки не меняются: после отказа документ остаётся в состоянии «Начальный ввод» и его приходится отменять руками, а после приёма остаток на счёте появляется, но документ в конечное состояние не переходит (проверено 14.09.2026 на документах 75070271 и 75070273). Ниже — что джобе нужно делать по каждому событию. Документ всегда находится по <code>GUID</code>, строка — по <code>GUID + REFERENCE</code>; событие без <code>REFERENCE</code> относится ко всему документу.</p>
|
||
<table>
|
||
<thead><tr><th style="width:230px">Событие</th><th style="width:70px">Уровень</th><th>Что сделать в DEPO</th><th style="width:230px">Конечное состояние</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>BJ_RECEIVED</code>, <code>BJ_SENT</code></td><td>документ</td><td>документ уже создан по шапке; ничего не делать, пометить SUCCESS</td><td>«Начальный ввод» (0) → можно «Начало обработки» (1)</td></tr>
|
||
<tr><td><code>BJ_DECISION_REJECT</code> (входящий: мы отказали), <code>BJ_CP_REJECT</code> (исходящий: отказал контрагент)</td><td>строка</td><td>в <code>D_CLISER_M2M_DOL</code> строки записать код из <code>M2M_REJECT_CODE</code> в <code>M2M_STATUS</code>/<code>M2M_TEXT</code> (текст из COMMENT). Если отказаны все строки документа — выполнить шаг «Отмена» (тип шага 0→REJ), как это делает оператор вручную</td><td>строка «отказ, M2Mxx»; документ «Отмена обработки» (3)</td></tr>
|
||
<tr><td><code>BJ_DECISION_CONFIRM</code>, <code>BJ_CP_CONFIRM</code></td><td>строка</td><td>отметить строку как принятую (<code>M2M_STATUS</code> «принято»); движения по счёту пока нет — ждём поручение НРД</td><td>строка «принято»; документ «Начало обработки» (1)</td></tr>
|
||
<tr><td><code>BJ_ORDER_SENT</code></td><td>документ</td><td>записать номер депо-поручения НРД (CONTR_D_ID из COMMENT); состояние — ожидание</td><td>«Ожидание доставки (ответа)» (10) или «Ожидание исполнения операции» (127)</td></tr>
|
||
<tr><td><code>BJ_ORDER_ACCEPTED</code> + <code>BJ_EXECUTED</code></td><td>документ</td><td><strong>провести движение по счёту депо</strong> по всем принятым строкам: входящий — зачисление на торговый счёт клиента (то, что оператор сделал вручную 14.09 для 75070271: появился остаток 10 SBER на 836), исходящий — списание; затем закрыть документ</td><td>«Конец обработки (в архиве)» (2)</td></tr>
|
||
<tr><td><code>BJ_GATEWAY_REJECT</code></td><td>строка</td><td>в <code>D_CLISER_M2M_DOL</code> строки записать код из <code>M2M_REJECT_CODE</code> и причину из COMMENT; если отказаны все строки — шаг «Отмена». Документ мог не создаться (например, бумаги с таким ISIN нет в OD_VALUES) — тогда достаточно пометить событие ERROR с текстом причины</td><td>строка «отказ шлюза»; документ «Отмена обработки» (3)</td></tr>
|
||
<tr><td><code>BJ_ORDER_REJECTED</code>, <code>BJ_SERVICE_ERROR</code>, <code>BJ_CANCELLED</code></td><td>документ</td><td>записать причину (COMMENT / код) в документ, выполнить шаг «Отмена»</td><td>«Отмена обработки» (3)</td></tr>
|
||
<tr><td><code>BJ_ORDER_LATE</code></td><td>документ</td><td>только пометка/комментарий («отчёт НРД не получен за 5 минут»); состояние не менять</td><td>без изменений</td></tr>
|
||
<tr><td><code>BJ_QUIK_*</code>, <code>BJ_PING</code></td><td>служебные</td><td>ничего не делать, пометить SUCCESS</td><td>без изменений</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="muted">Названия и коды состояний — из справочника <code>OD_DOC_STATES</code> (D_CAT=0): 0 «Начальный ввод», 1 «Начало обработки (зарегистрирован)», 2 «Конец обработки (в архиве)», 3 «Отмена обработки», 10 «Ожидание доставки (ответа)», 127 «Ожидание исполнения операции». Шаг отмены, который выполнял оператор: <code>OD_STEP_TYPES</code> ID 75069472 «Отмена» (SYS_NAME <code>0->REJ</code>, OUT_STATE 3).</p>
|
||
<p>Запрос, которым джоба находит отказы вместе с документом и строкой:</p>
|
||
<pre class="copyable">SELECT e.ID, e.GUID, e.REFERENCE, e.M2M_REJECT_CODE, e.COMMENT, m.DOC, l.DOL
|
||
FROM dbo.FTV_M2M_EVENTS e
|
||
JOIN dbo.FTV_M2M m ON m.GUID = e.GUID
|
||
JOIN dbo.FTV_M2M_LINES l ON l.GUID = e.GUID AND l.REFERENCE = e.REFERENCE
|
||
WHERE e.STATE = 'TODO' AND e.EVENT_TYPE IN ('BJ_DECISION_REJECT', 'BJ_CP_REJECT')
|
||
ORDER BY e.ID;</pre>
|
||
<p>Для событий по документу целиком (REFERENCE пуст) джойн со строками не нужен:</p>
|
||
<pre class="copyable">SELECT e.ID, e.GUID, e.EVENT_TYPE, e.M2M_REJECT_CODE, e.COMMENT, m.DOC
|
||
FROM dbo.FTV_M2M_EVENTS e
|
||
JOIN dbo.FTV_M2M m ON m.GUID = e.GUID
|
||
WHERE e.STATE = 'TODO' AND e.REFERENCE IS NULL
|
||
ORDER BY e.ID;</pre>
|
||
<p><strong>Обратная связь в bj.</strong> bj сам читает состояние документа по номеру (<code>OD_DOCS.STATE</code> по <code>FTV_M2M.DOC</code>) и показывает его в карточке перевода; при ручной отмене в DEPO оператор bj получает оповещение. Поэтому отдельные события от джобы о смене состояния не требуются — достаточно, чтобы документ переходил в состояния из таблицы выше.</p>
|
||
|
||
<h2 id="handbook">Справочник участников M2M (M2MTH) → таблица FTV_M2M_HANDBOOK</h2>
|
||
<p>НРД ведёт справочник участников переводов по MOEX МОСТ: кто депозитарий, кто брокер, их коды участника и ИНН, плюс места расчётов. bj получает его от НРД (сообщение M2MTransferHandbook) при старте и обновляет раз в сутки; по нему bj определяет контрагента по ИНН из перевода и наполняет справочник депозитариев в QUIK МНП. Предлагаем отдавать его в DEPO целиком в отдельную таблицу — тогда <code>CONTRAGENT_M2M_CODE</code> из FTV_M2M раскрывается в ИНН и наименование, и по нему можно заводить контрагентов в DEPO.</p>
|
||
<p class="muted">Сейчас в справочнике 158 участников и 2 мест расчётов (получено 2026-09-09). Наш код участника — <code>MC0413600000</code>.</p>
|
||
<table>
|
||
<thead><tr><th style="width:190px">Поле таблицы</th><th style="width:130px">Тип</th><th>Откуда у bj / что означает</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>INN</code></td><td>varchar(12)</td><td>ИНН организации. Вместе с <code>PARTICIPANT_CODE</code> и <code>KIND</code> — ключ строки.</td></tr>
|
||
<tr><td><code>PARTICIPANT_CODE</code></td><td>varchar(12)</td><td>код участника МОСТ (<code>MC…</code>, 12 знаков) — тот самый <code>CONTRAGENT_M2M_CODE</code>. У мест расчётов пусто.</td></tr>
|
||
<tr><td><code>KIND</code></td><td>varchar(8)</td><td><code>DEPO</code> — депозитарий, <code>BROKER</code> — брокер (одна организация может быть в обоих качествах — две строки), <code>PLACE</code> — место расчётов.</td></tr>
|
||
<tr><td><code>NAME_FULL</code></td><td>nvarchar(255)</td><td>полное наименование (рус.)</td></tr>
|
||
<tr><td><code>NAME_SHORT</code></td><td>nvarchar(128)</td><td>краткое наименование (рус.)</td></tr>
|
||
<tr><td><code>NAME_DISPLAY</code></td><td>nvarchar(255)</td><td>отображаемое наименование (рус.) — то, что показываем в интерфейсах</td></tr>
|
||
<tr><td><code>NAME_ENG</code></td><td>nvarchar(255)</td><td>наименование на английском (есть не у всех)</td></tr>
|
||
<tr><td><code>HANDBOOK_AT</code></td><td>datetime</td><td>дата/время версии справочника НРД (CreationTimestamp)</td></tr>
|
||
<tr><td><code>UPDATED_AT</code></td><td>datetime</td><td>когда bj записал строку</td></tr>
|
||
<tr><td><code>ACTIVE</code></td><td>bit</td><td>всегда 1 (история не ведётся, таблица перезаписывается целиком)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Обновление (согласовано).</strong> История не нужна: при каждом получении справочника от НРД bj удаляет все строки и записывает содержимое заново одной транзакцией (DEPO не видит пустую таблицу; DELETE вместо TRUNCATE, потому что TRUNCATE требует права ALTER) — аналог нового HANDBOOK.xml. Дополнительно перезапись повторяется при старте bj и раз в 6 часов. <strong>Просьба создать таблицу по скрипту ниже</strong> (создаёт наша сторона: у логина bj нет права CREATE TABLE в BJ_DEPO_DATA). Поля — как в описании; bj2depo использует <code>INN</code>, <code>PARTICIPANT_CODE</code>, <code>NAME_SHORT</code>. Английские наименования и места расчётов bj пишет, джоба их не использует.</p>
|
||
<p>Примеры реальных строк:</p>
|
||
<table><thead><tr><th>INN</th><th>PARTICIPANT_CODE</th><th>KIND</th><th>NAME_DISPLAY</th></tr></thead><tbody><tr><td><code>7707025725</code></td><td><code>MC0005600000</code></td><td>DEPO</td><td>АО АКБ "ЦентроКредит"</td></tr><tr><td><code>9714012520</code></td><td><code>MC0418000000</code></td><td>DEPO</td><td>ИК ФИАНИТ</td></tr><tr><td><code>7831000034</code></td><td>—</td><td>PLACE</td><td>АО "СПБ Банк"</td></tr>
|
||
</tbody></table>
|
||
<p class="muted">Как это связано с остальным: в FTV_M2M поле <code>CONTRAGENT_M2M_CODE</code> = <code>PARTICIPANT_CODE</code> строки с <code>KIND=DEPO</code>; ИНН отправителя/получателя в сообщениях НРД (<code>TransferringDepository/ReceivingDepository INN</code>) = <code>INN</code>. Тот же справочник bj загружает в QUIK МНП (таблица <code>M2MDepoRecipients</code>: ИНН + наименование).</p>
|
||
|
||
<h2 id="runmode">Кто запускает обработку: джоба SQL Agent или bj</h2>
|
||
<p>Обработка трансферных таблиц — это вызов процедуры <code>FTV_M2M_JOB</code> в базе DEPO. Есть два равноценных способа её запускать:</p>
|
||
<table>
|
||
<thead><tr><th style="width:230px">Способ</th><th>Что нужно на сервере клиента</th><th>Плюсы / минусы</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><strong>A. Джоба SQL Agent</strong> <code>bj2depo</code> (как сейчас у нас: шаг <code>exec FTV_M2M_JOB</code>, каждые 10 с)</td><td>SQL Agent запущен; права на создание джобы (SQLAgentOperatorRole / sysadmin); учётка Agent с правами на BJ_DEPO_DATA</td><td>работает независимо от bj; но у каждого клиента нужно заводить джобу и права, а bj не видит её состояние без доступа к msdb</td></tr>
|
||
<tr><td><strong>B. bj вызывает процедуру сам</strong> (Настройка → Обмен Fancy → «Обработка трансферных таблиц DEPO»)</td><td>только <code>GRANT EXECUTE ON dbo.FTV_M2M_JOB TO <логин bj></code></td><td>одна настройка на все внедрения, состояние и ошибки видны в bj; обработка идёт, только пока работает bj</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="muted">Включать нужно один вариант, иначе процедура будет вызываться параллельно из двух мест. Просьба к разработчику: сделать <code>FTV_M2M_JOB</code> безопасной при повторном/параллельном вызове (обработка только строк со <code>STATE='TODO'</code>, блокировка строки на время обработки) — тогда случайное двойное включение не навредит.</p>
|
||
|
||
|
||
<h2 id="replay">Результат выгрузки истории тестов (14.09.2026)</h2>
|
||
<p>Кнопкой «Выгрузить историю в DEPO» на вкладке «Тестирование» bj записал в трансферные таблицы все переводы, проведённые на TEST3 (робот НРД и банк Зенит): 78 переводов, 252 события с реальными датами. Джоба bj2depo создала по каждому документ в DEPO (D_CLISER_M2M + строки), сама определила контрагента, депонента, бумагу и место хранения. Что при этом выяснилось:</p>
|
||
<ul>
|
||
<li><strong>События по строкам</strong> (<code>BJ_DECISION_*</code>, <code>BJ_CP_*</code>, с REFERENCE) джоба обрабатывает — SUCCESS.</li>
|
||
<li><strong>События по поручению целиком</strong> (REFERENCE пуст: <code>BJ_SENT</code>, <code>BJ_RECEIVED</code>, <code>BJ_SERVICE_ERROR</code>, <code>BJ_ORDER_SENT</code>, <code>BJ_ORDER_ACCEPTED</code>, <code>BJ_EXECUTED</code>) джоба отвергает с текстом «Не найдена строка M2M-поручение по GUID + REFERENCE». <strong>Просьба:</strong> для событий без REFERENCE применять действие к документу (или ко всем его строкам).</li>
|
||
<li>После обработки событий статусы в DEPO не меняются: у всех документов <code>DOC_STATUS=0</code>, у строк <code>M2M_STATUS=0</code>, <code>DEP_STATUS=0</code>. <strong>Просьба согласовать отображение</strong>, например: <code>BJ_CP_REJECT</code> / <code>BJ_DECISION_REJECT</code> → строка «отказ» с кодом в M2M_TEXT; <code>BJ_CP_CONFIRM</code> / <code>BJ_DECISION_CONFIRM</code> → «принято»; <code>BJ_ORDER_ACCEPTED</code> / <code>BJ_EXECUTED</code> → документ «исполнен»; <code>BJ_SERVICE_ERROR</code> / <code>BJ_CANCELLED</code> → документ «отклонён / отменён». Тогда историю тестов можно будет просмотреть в DEPO с настоящими статусами.</li>
|
||
<li>Событие <code>BJ_PING</code> сейчас помечается ERROR — достаточно помечать SUCCESS без действий.</li>
|
||
</ul>
|
||
|
||
<h2 id="sql">Примеры SQL</h2>
|
||
<p>Так bj заведёт входящий перевод (одна транзакция):</p>
|
||
<pre class="copyable">BEGIN TRAN;
|
||
INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC, DEPONENT_ID, COMMENT)
|
||
VALUES ('5325B860-3265-49B6-986D-FA84FB41FE27', 'IN', 'MC0034900000', N'Погонщикова Виктория Олеговна', '4500 123456', 11773497, N'M2MTR от MC0034900000');
|
||
INSERT INTO dbo.FTV_M2M_LINES (GUID, REFERENCE, ISIN, SEC_CODE, SHARE, AMOUNT, PLACE_NUMBER)
|
||
VALUES ('5325B860-3265-49B6-986D-FA84FB41FE27', 'M2M0000002675550', 'RU000A107UL4', 'RU000A107UL4', 18824, 1, '36MC0413600000F00');
|
||
INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, STATE, EVENT_TYPE, EVENT_DATE, COMMENT)
|
||
VALUES ('5325B860-3265-49B6-986D-FA84FB41FE27', NULL, 'TODO', 'BJ_RECEIVED', GETDATE(), N'входящий перевод получен');
|
||
COMMIT;</pre>
|
||
<p>Так bj2depo сообщает, что документ в DEPO создан:</p>
|
||
<pre class="copyable">INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, STATE, EVENT_TYPE, EVENT_DATE, COMMENT)
|
||
VALUES ('5325B860-3265-49B6-986D-FA84FB41FE27', NULL, 'TODO', 'DEPO_ORDER_CREATED', GETDATE(), N'75070271');</pre>
|
||
<p>Так bj забирает свои события и подтверждает обработку:</p>
|
||
<pre class="copyable">SELECT ID, GUID, REFERENCE, EVENT_TYPE, M2M_REJECT_CODE, COMMENT
|
||
FROM dbo.FTV_M2M_EVENTS WHERE EVENT_TYPE LIKE 'DEPO_%' AND STATE = 'TODO' ORDER BY ID;
|
||
UPDATE dbo.FTV_M2M_EVENTS SET STATE = 'SUCCESS', TimeBeg = GETDATE(), TimeEnd = GETDATE(), COMMENT = N'применено в bj' WHERE ID = @id;</pre>
|
||
|
||
<p>Структура таблицы справочника и то, как bj её перезаписывает:</p>
|
||
<pre class="copyable">CREATE TABLE dbo.FTV_M2M_HANDBOOK (
|
||
ID int IDENTITY(1,1) PRIMARY KEY, INN varchar(12) NOT NULL, PARTICIPANT_CODE varchar(12) NULL, KIND varchar(8) NOT NULL,
|
||
NAME_FULL nvarchar(255) NULL, NAME_SHORT nvarchar(128) NULL, NAME_DISPLAY nvarchar(255) NULL, NAME_ENG nvarchar(255) NULL,
|
||
HANDBOOK_AT datetime NULL, UPDATED_AT datetime NOT NULL DEFAULT GETDATE(), ACTIVE bit NOT NULL DEFAULT 1);
|
||
-- при каждом получении справочника от НРД (одна транзакция):
|
||
BEGIN TRAN;
|
||
DELETE FROM dbo.FTV_M2M_HANDBOOK;
|
||
INSERT INTO dbo.FTV_M2M_HANDBOOK (INN, PARTICIPANT_CODE, KIND, NAME_FULL, NAME_SHORT, NAME_DISPLAY, NAME_ENG, HANDBOOK_AT, UPDATED_AT, ACTIVE)
|
||
VALUES ('7707025725', 'MC0005600000', 'DEPO', N'…', N'АО АКБ "ЦентроКредит"', N'…', N'…', @hb_at, GETDATE(), 1); -- и так по всем участникам
|
||
COMMIT;</pre>
|
||
|
||
<h2 id="questions">Открытые вопросы</h2>
|
||
<p class="muted">Закрыто 15.09.2026:</p>
|
||
<ul>
|
||
<li>от bj2depo достаточно <code>DEPO_ORDER_CREATED</code> и <code>DEPO_ERROR</code>; <code>DEPO_EXECUTED</code> не нужен, bj его не ждёт;</li>
|
||
<li>справочник: поля как в описании, джоба использует <code>INN</code> / <code>PARTICIPANT_CODE</code> / <code>NAME_SHORT</code>, история не нужна, перезапись целиком; таблицу создаёт наша сторона;</li>
|
||
<li><code>REFERENCE</code> для всех исходящих переводов, включая созданные из терминала QUIK, присваивает bj при формировании M2MTR (формат <code>M2M…</code>);</li>
|
||
<li>джоба <code>bj2depo</code> на SQL002 / <code>BJ_DEPO_DATA</code> работает (EXECUTE AS SYSDBA), логин bj — читатель базы и запись в трансферные таблицы;</li>
|
||
<li>события <code>BJ_QUIK_*</code> bj пишет для истории; джоба помечает их SUCCESS без действий.</li>
|
||
</ul>
|
||
<p>Остаётся:</p>
|
||
<ol>
|
||
<li><strong>Создать таблицу <code>FTV_M2M_HANDBOOK</code></strong> в BJ_DEPO_DATA по скрипту из раздела «Справочник участников».</li>
|
||
<li>Действия джобы по событиям (раздел «Действия джобы»): движение по счёту депо по <code>BJ_ORDER_ACCEPTED</code> / <code>BJ_EXECUTED</code>, запись отказов в строки и шаг «Отмена».</li>
|
||
</ol>
|
||
</article>
|
||
</div>
|
||
{{end}}
|