package lkgateway
// outgoing_export.go — выгрузка для регулятора по исходящему переводу (списание):
// отчёт-доказательство с хронологией депозитарного исполнения (черновик 16 →
// поручение 16 подано → отчёт MS010 о списании) + дословный XML отправленного 16.
import (
"archive/zip"
"fmt"
"html"
"net/http"
"strings"
"time"
)
func exportOutgoing(w http.ResponseWriter, r *http.Request, deps outgoingDeps, guid string) {
deal, ok := scanForm16Deal(deps.store, guid)
if !ok {
http.Error(w, "исходящая сделка не найдена", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="ishodyaschiy-%s.zip"`, shortID(guid)))
zw := zip.NewWriter(w)
defer zw.Close()
if f, e := zw.Create("otchet.html"); e == nil {
_, _ = f.Write(outgoingReportHTML(deal))
}
if deal.OrderXML != "" {
if f, e := zw.Create("poruchenie-16.xml"); e == nil {
_, _ = f.Write(faithfulXMLBytes(deal.OrderXML))
}
}
// Ответ НРД по поручению (отчёт MS010 + квитанции) — сырые файлы из INBOX.
for _, rf := range collectOutgoingResponseRaw(deal.ContrDIDs) {
if f, e := zw.Create("otvet-nrd/" + rf.Name); e == nil {
_, _ = f.Write(rf.Data)
}
}
}
func outgoingReportHTML(d *OutgoingDeal) []byte {
esc := html.EscapeString
msk := func(t time.Time) string {
if t.IsZero() {
return "—"
}
return t.In(time.FixedZone("МСК", 3*3600)).Format("02.01.2006 15:04:05") + " МСК"
}
var b strings.Builder
b.WriteString(`
`)
b.WriteString(`Отчёт об исходящем переводе M2M (списание)`)
b.WriteString(`` + moexMostLogoSVG +
`Сервис быстрых переводов ценных бумаг
MOEX МОСТ · НКО АО НРД` + orgLogoIMG() + `
`)
b.WriteString(`Отчёт об исходящем переводе ценных бумаг (списание, M2M / MOEX МОСТ)
`)
b.WriteString(`Доказательство для контролирующих органов. GUID сделки: ` + esc(d.GUID) +
` · Сформировано: ` + time.Now().In(time.FixedZone("МСК", 3*3600)).Format("02.01.2006 15:04:05") + ` МСК
`)
row := func(k, v string) { b.WriteString(`| ` + k + ` | ` + v + ` |
`) }
b.WriteString(`Реквизиты перевода
`)
row("GUID сделки", ``+esc(d.GUID)+``)
if d.DealRef != "" {
row("Референс сделки (deal_reference)", esc(d.DealRef))
}
to := esc(d.CounterpartyCode)
if n := handbookNameByCode(d.CounterpartyCode); n != "" {
to = `` + esc(n) + ` · ` + to
}
row("Принимающий депозитарий (контрагент)", to)
b.WriteString(`
`)
var ms010At string
for _, rp := range d.Responses {
if rp.Done {
ms010At = rp.Time
}
}
b.WriteString(`Депозитарное исполнение (поручение 16 — списание) — хронология
`)
row("Черновик 16 получен от НРД (MF010)", msk(d.DraftAt))
if d.Submitted {
row("Поручение 16 подано в НРД", msk(d.OrderAt))
row("Тип ЭД / пакет(ы) ИШ", "ORDXM · "+esc(d.Pkgs))
if len(d.ContrDIDs) > 0 {
row("Номера поручений (contr_d_id)", esc(strings.Join(d.ContrDIDs, ", ")))
}
} else {
row("Поручение 16 подано в НРД", `— (ещё не подано)`)
}
if ms010At != "" {
row("Отчёт MS010 получен (списание проведено)", esc(ms010At)+` МСК — бумага списана и передана контрагенту`)
} else {
row("Отчёт MS010 получен", `— (ещё не получен)`)
}
b.WriteString(`
`)
b.WriteString(`Ценные бумаги
| Код НРД / ISIN | Кол-во | № поручения |
`)
for _, s := range d.Secs {
code := esc(s.SecurityCode)
if s.ISIN != "" && s.ISIN != s.SecurityCode {
code += ` / ` + esc(s.ISIN)
}
b.WriteString(`| ` + code + ` | ` + esc(s.Quantity) + ` | ` + esc(s.ContrDID) + ` |
`)
}
b.WriteString(`
`)
if len(d.Responses) > 0 {
b.WriteString(`Ответы НРД по поручению
| Время получения | Тип ответа | Пакет |
`)
for _, rp := range d.Responses {
b.WriteString(`| ` + esc(rp.Time) + ` МСК | ` + esc(rp.Kind) + ` | ` + esc(rp.Pkg) + ` |
`)
}
b.WriteString(`
`)
}
if d.OrderXML != "" {
b.WriteString(`Отправленное поручение 16 (дословно)
` + esc(d.OrderXML) + `
`)
}
b.WriteString(`Отчёт сформирован автоматически системой Bridge-and-Join-s (bj). Логотип MOEX МОСТ — собственность НКО АО НРД.
`)
b.WriteString(``)
return []byte(b.String())
}