Анкета участника: что не известно точно — уточнить у НРД
Источник наименований: карточка нашей фирмы в справочнике НРД, без неё — DEPO (черновик). Анкета предупреждает и даёт готовый текст запроса в НРД, если карточки нет, полное похоже на краткое, отличается от карточки или английские неполные; при ответе M2M37 — подсказка запросить наименования. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
@@ -146,6 +146,59 @@ func participantFormValidate(d participantFormData) []string {
|
||||
return errs
|
||||
}
|
||||
|
||||
// pfAbbrRe — наименование начинается с сокращённой формы (ООО, АО…) — для полного это ошибка.
|
||||
var pfAbbrRe = regexp.MustCompile(`^(ООО|ОАО|ЗАО|ПАО|НАО|АО|АКБ|КБ|НКО|ИК)[\s"«]`)
|
||||
|
||||
// pfAskNSD — текст запроса в НРД о точных наименованиях участника (ответ НРД — источник истины).
|
||||
func pfAskNSD(code string) string {
|
||||
if code == "" {
|
||||
code = "наш код участника"
|
||||
}
|
||||
return "Просим сообщить точное наименование, под которым участник " + code +
|
||||
" зарегистрирован в клиентской базе НРД: полное (FullName), сокращённое (ShortName), " +
|
||||
"а также английские полное и сокращённое, если они есть."
|
||||
}
|
||||
|
||||
// pfNameCheck — что в наименованиях надо уточнить у НРД. НРД сверяет их со своей клиентской
|
||||
// базой (M2M37): точный источник — карточка нашей фирмы в справочнике НРД или ответ НРД;
|
||||
// данные DEPO — только черновик.
|
||||
func pfNameCheck(rc *RuntimeConfig, d participantFormData) []string {
|
||||
var w []string
|
||||
hb, ok := participantCard(rc, d.INN)
|
||||
switch {
|
||||
case ok && strings.TrimSpace(hb.FullName) != "":
|
||||
x := participantFormData{FullName: hb.FullName}
|
||||
pfNames(&x)
|
||||
if x.FullName != d.FullName {
|
||||
w = append(w, "Полное наименование отличается от карточки нашей фирмы в справочнике НРД: «"+x.FullName+"». НРД сверяет с ней — без причины не меняйте.")
|
||||
}
|
||||
default:
|
||||
w = append(w, "Наименования взяты из DEPO, в справочнике НРД нашей карточки нет. НРД сверяет их со своей клиентской базой — уточните точные наименования у НРД и впишите как есть.")
|
||||
}
|
||||
if pfAbbrRe.MatchString(d.FullName) || (d.FullName != "" && d.FullName == d.ShortName) {
|
||||
w = append(w, "Полное наименование похоже на сокращённое — нужно полное («Общество с ограниченной ответственностью …»), как в клиентской базе НРД.")
|
||||
}
|
||||
if (d.EngShort != "" || d.EngDisplay != "") && d.EngFull == "" {
|
||||
w = append(w, "Английские: указано краткое без полного — уточните полное у НРД или оставьте английские пустыми.")
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// participantCard — карточка нашей фирмы в справочнике НРД: по MC-коду, затем по ИНН.
|
||||
func participantCard(rc *RuntimeConfig, inn string) (HandbookEntry, bool) {
|
||||
if rc == nil {
|
||||
return HandbookEntry{}, false
|
||||
}
|
||||
s := rc.Snapshot()
|
||||
if hb, ok := handbookByCode(orgCode(s)); ok {
|
||||
return hb, true
|
||||
}
|
||||
if inn = strings.TrimSpace(pick(inn, orgINN(s))); inn != "" {
|
||||
return handbookByINN(inn)
|
||||
}
|
||||
return HandbookEntry{}, false
|
||||
}
|
||||
|
||||
// pfQuotes — кавычки как в клиентской базе НРД: прямые ".
|
||||
var pfQuotes = strings.NewReplacer("«", `"`, "»", `"`, "“", `"`, "”", `"`, "„", `"`)
|
||||
|
||||
@@ -376,7 +429,16 @@ func registerParticipantForm(mux *http.ServeMux, rc *RuntimeConfig, igwClient *i
|
||||
}
|
||||
|
||||
xmlPreview := participantFormXML(d)
|
||||
participantFormRender(w, d, errs, action, flash, flashKind, xmlPreview, participantRegStatus(rc), igwClient != nil && igwChannel != "", isProdIGW(igwChannel))
|
||||
reg := participantRegStatus(rc)
|
||||
if ws := pfNameCheck(rc, d); len(ws) > 0 {
|
||||
code := ""
|
||||
if rc != nil {
|
||||
code = orgCode(rc.Snapshot())
|
||||
}
|
||||
reg += `<div class="warn"><b>Уточните у НРД:</b><ul><li>` + strings.Join(escapeAll(ws), "</li><li>") +
|
||||
`</li></ul>Запрос в НРД (можно скопировать):<pre>` + html.EscapeString(pfAskNSD(code)) + `</pre></div>`
|
||||
}
|
||||
participantFormRender(w, d, errs, action, flash, flashKind, xmlPreview, reg, igwClient != nil && igwChannel != "", isProdIGW(igwChannel))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -400,6 +462,9 @@ func participantRegStatus(rc *RuntimeConfig) string {
|
||||
out.WriteString(`Ответ НРД пока не получен.`)
|
||||
case strings.EqualFold(reg.Status, "ERROR"):
|
||||
out.WriteString(`<b style="color:#b00">НРД отклонил</b> ` + reg.RespAt.Format("02.01 15:04") + `: ` + html.EscapeString(reg.RespText) + `. Исправьте указанное и отправьте снова.`)
|
||||
if containsStr(reg.Codes, "M2M37") {
|
||||
out.WriteString(` Наименование не совпало с клиентской базой НРД — запросите у НРД точные наименования (текст запроса ниже) и впишите их без изменений.`)
|
||||
}
|
||||
default:
|
||||
out.WriteString(`<b style="color:#2a7a2a">НРД принял</b> ` + reg.RespAt.Format("02.01 15:04") + `. ` + html.EscapeString(reg.RespText))
|
||||
}
|
||||
@@ -467,18 +532,20 @@ button.primary{background:#1763cc;color:#fff;border-color:#1763cc}
|
||||
pre{background:#f6f8fa;border:1px solid #ddd;border-radius:8px;padding:12px;overflow:auto;white-space:pre-wrap}
|
||||
.ok{background:#e7f6e7;border:1px solid #58a858;padding:8px 12px;border-radius:6px;margin:10px 0}
|
||||
.bad{background:#fdecec;border:1px solid #d66;padding:8px 12px;border-radius:6px;margin:10px 0}
|
||||
.note{color:#555;font-size:13px}</style></head><body>
|
||||
.note{color:#555;font-size:13px}
|
||||
.warn{background:#fff7e0;border:1px solid #e0b040;padding:8px 12px;border-radius:6px;margin:10px 0}.warn pre{margin:6px 0 0}</style></head><body>
|
||||
<h1>Регистрация участника сервиса M2M (МОСТ)</h1>
|
||||
<p class="note">Требование НРД при подключении: зарегистрировать организацию в справочнике участников M2M.
|
||||
Без регистрации сервис отбивает запросы кодом <b>M2M14</b>. В боевом контуре анкета подаётся <b>через ИШ</b>
|
||||
пакетом <code>#M2MPF</code> (businessLine=MOST, подпись боевым контейнером на стороне ИШ); письмо на
|
||||
<code>` + participantFormRecipient + `</code> оставлено запасным каналом. Поля предзаполнены из
|
||||
автоопределения DEPO — проверьте юридические реквизиты.</p>
|
||||
<code>` + participantFormRecipient + `</code> оставлено запасным каналом. Наименования должны точно совпадать с
|
||||
клиентской базой НРД (иначе <b>M2M37</b>): bj берёт их из карточки нашей фирмы в справочнике НРД, а если её нет — из DEPO.
|
||||
Чего нет или в чём bj не уверен — уточните у НРД. Кавычки — прямые <code>"</code>.</p>
|
||||
` + regHTML + flashHTML + vErr + `
|
||||
<form method="post">
|
||||
<fieldset><legend>Организация</legend>
|
||||
` + inp("inn", "ИНН (10 цифр)", d.INN, "7700000000") + `
|
||||
` + inp("full", "Полное наименование (по уставу)", d.FullName, "") + `
|
||||
` + inp("full", "Полное наименование (как в клиентской базе НРД)", d.FullName, "Общество с ограниченной ответственностью …") + `
|
||||
<div class="row">` + inp("short", "Сокращённое", d.ShortName, "") + inp("display", "Отображаемое (DisplayName)", d.DisplayName, "") + `</div>
|
||||
</fieldset>
|
||||
<fieldset><legend>Английские наименования (опционально)</legend>
|
||||
|
||||
@@ -27,3 +27,15 @@ func TestParticipantFormNames(t *testing.T) {
|
||||
t.Error("пустые английские наименования не должны попадать в анкету")
|
||||
}
|
||||
}
|
||||
|
||||
// Без карточки НРД и с кратким вместо полного — подсказка уточнить у НРД.
|
||||
func TestParticipantNameCheck(t *testing.T) {
|
||||
d := participantFormData{FullName: `ООО "ИК "Фонтвьель"`, ShortName: `ООО "ИК "Фонтвьель"`}
|
||||
ws := pfNameCheck(nil, d)
|
||||
if len(ws) != 2 || !strings.Contains(ws[0], "уточните") || !strings.Contains(ws[1], "сокращённое") {
|
||||
t.Fatalf("подсказки: %q", ws)
|
||||
}
|
||||
if !strings.Contains(pfAskNSD("MC0413600000"), "MC0413600000") {
|
||||
t.Error("в запросе нет кода участника")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user