Анкета участника M2M: сформировать → проверить → отправить в НРД (обычный канал)

Требование НРД при подключении к сервису M2M (МОСТ): зарегистрировать организацию
в справочнике участников анкетой M2MTransferParticipantForm, иначе сервис отбивает
запросы кодом M2M14. Регистрация идёт ОБЫЧНЫМ каналом (письмом на M2MOST@nsd.ru с
вложением), не через M2M-шлюз.

- Новая страница /admin/setup/participant-form (participant_form.go):
  предзаполнение из автоопределения DEPO (ИНН, наименования, код депозитария),
  правка оператором, валидация (формат ИНН/кодов + m2m.HandbookParticipant.Validate),
  предпросмотр анкеты, «Скачать» (windows-1251) и «Отправить в НРД» письмом с
  вложением-анкетой.
- Генерация XML строкой-шаблоном по образцу НРД (namespace participant/form,
  CreationTimestamp по МСК, блоки Eng/BrokerPlace — опциональны), кодировка
  windows-1251 через nsdxml.EncodeWindows1251; экранирование текста только & < >.
- Почта: выделен транспорт smtpDeliver из sendSMTP; добавлены buildMIMEAttach
  (multipart/mixed + base64-вложение) и sendSMTPAttach.
- Тесты: валидация, генерация XML (Eng/BrokerPlace), cp1251 round-trip, MIME-вложение.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XG3zNMMf2LMoDpXo22mwMX
This commit is contained in:
fontvielle
2026-10-15 18:20:03 +03:00
parent c476cfd7a3
commit c021fb8273
4 changed files with 463 additions and 1 deletions
+7 -1
View File
@@ -61,12 +61,18 @@ func sendSMTP(s SMTPSettings, subject, body string) error {
if len(rcpts) == 0 {
return fmt.Errorf("не заданы получатели (To)")
}
return smtpDeliver(s, rcpts, buildMIME(s.From, rcpts, subject, body))
}
// smtpDeliver — транспорт SMTP: соединение, TLS, аутентификация и отправка
// готового MIME-сообщения. Выделено из sendSMTP для переиспользования в
// письмах с вложением (sendSMTPAttach, participant_form.go).
func smtpDeliver(s SMTPSettings, rcpts []string, msg []byte) error {
port := s.Port
if port == 0 {
port = 587
}
addr := net.JoinHostPort(s.Host, strconv.Itoa(port))
msg := buildMIME(s.From, rcpts, subject, body)
dialer := &net.Dialer{Timeout: 15 * time.Second}
var client *smtp.Client