# PR-1: Go-модели M2M + парсер windows-1251 + round-trip тесты ## Цель Реализовать типизированную доменную модель сообщений M2M по XSD НРД, парсер/сериализатор XML в windows-1251 и round-trip тесты на эталонах. Это первый осмысленный код в проекте, на нём основываются все последующие модули (`nsd-adapter`, `m2m-core`, `lk-gateway`, `lk-emulator`). ## Источники правды - `DOC/M2MSchemas_260408/*.xsd` — все типы и структуры, namespace `http://nsd.ru/schemas/m2m/...`, version `2026-04-08`. - `DOC/Примеры/*.xml` — примеры всех типов сообщений (windows-1251). - `DOC/Эталонные сообщения/*.xml` — эталоны для приёмочной проверки. ## Состав PR ### 1. `internal/m2m/types.go` Все simple-типы и enum'ы из XSD как Go-типы. **Enum'ы (со списком допустимых значений):** - `StatusCode` — `INFO | ERROR`. - `IIAContractType` — **`T12 | T03`** (именно так — T12 = обмен ИИС-1/ИИС-2, T03 = ИИС-3; не T01/T02/T03). - `SecurityClassification` — `BOND | SHAR | MFUN`. - `SecurityCategory` — `ORDN | PREF | UKWN`. - `IdentityDocumentCode` — `01 | 02 | 03 | 04 | 05 | 06 | 07 | 09 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 26 | 27 | 91`. - `IsolationStatus` — только `SGDN`. **Simple-типы как Go-обёртки над `string`:** - `DeponentCode`, `ReferenceId`, `ISIN`, `OrganizationINN`, `UUID`, `AccountId`, `SecurityCode`, `IdentityDocSerial`. ### 2. `internal/m2m/validators.go` Метод `Validate() error` на каждом типе из пункта 1, проверяющий pattern и длину из XSD: | Тип | Pattern | Длина | |-----|---------|-------| | `ReferenceId` | `^M2M[A-Z0-9]{13}$` | ровно 16 | | `ISIN` | `^[A-Z]{2}[A-Z0-9]{9}[0-9]$` | ровно 12 | | `OrganizationINN` | `^[0-9]{10}$` | ровно 10 | | `DeponentCode` | `^[A-Z0-9]+$` | до 12 | | `UUID` | стандартный UUID v1-5 pattern | 36 | | `SecurityCode` | `^[0-9A-Z_/-]+$` | ровно 12 | | `IdentityDocSerial` | `^\S+$` | от 1 | Enum'ы валидируются проверкой принадлежности к допустимому множеству. ### 3. `internal/m2m/messages.go` Структуры всех 6 типов сообщений M2M: - `M2MTransferRequest` — Header (UUID, NSDDateTime, SenderCode, ReceiverCode, CostInfo, опц. IIAAgreementDetails) + Data (IsM2M=true, InvestorInformation, TransferringDepository, ReceivingDepository, TransferredSecurities) + опц. NSDInfo. - `M2MTransferDecision` — Header (тот же набор минус IIAAgreementDetails) + Data (ReceivingDepository, Security[] с TransferDecision Choice). - `M2MTransferResponse` — GUID + StatusCode + Response[] (ReferenceId, Code, Text). - `M2MTransferHandbook` + `M2MTransferHandbookRequest`. - `M2MTransferParticipantForm`. **Choice-типы.** Реализуй через указатели на взаимоисключающие поля: - `CostInfo { Yes *Yes; No *No }` — ровно один не nil. - `Quantity { Whole *uint64; Fractional *Decimal16 }`. - `SecurityDetails { ISIN *ISIN; SecurityInfo *SecurityDescription }`. - `IdentificationDetails { RegNumber *string; FundShares *FundShares }`. - `DecisionTransfer { Confirmation *Confirmation; Rejection *Rejection }`. В `Validate()` каждого choice — проверка «ровно одно поле задано». **Фиксированные значения.** `IsM2M` всегда `true` — захардкодь в `MarshalXML` (не выноси в поле, заполняй автоматически). **XML-теги.** Для каждого поля укажи `xml:"..."` с правильным namespace из XSD. Используй namespace-aliases: - `rt` для `http://nsd.ru/schemas/m2m/request`, - `dn` для `http://nsd.ru/schemas/m2m/decision`, - `hk` для `http://nsd.ru/schemas/m2m/handbook`, - `hr` для `http://nsd.ru/schemas/m2m/handbook/request`, - `pf` для `http://nsd.ru/schemas/m2m/participant/form`, - `m2m` для `http://nsd.ru/schemas/m2m/types`. ### 4. `internal/nsdxml/datetime.go` Тип `NSDDateTime` для нестандартного формата НРД. - Формат: `YYYY-MM-DDThh:mm:ss(МСК[+-N])`, пример: `2026-03-02T14:30:45(МСК+2)`. - Regex из XSD: `^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\(МСК([+-][0-9]{1,2})?\)$`. - Внутреннее представление — `time.Time` в location `Europe/Moscow` плюс смещение (если есть). - Реализуй: `MarshalXML`, `UnmarshalXML`, `MarshalText`, `UnmarshalText`, `String() string`, `Now() NSDDateTime`. - Кейсы для тестов: `(МСК)`, `(МСК+2)`, `(МСК-1)`, `(МСК+12)`. ### 5. `internal/nsdxml/codec.go` Marshal/Unmarshal XML в windows-1251. - Зависимость: `golang.org/x/text/encoding/charmap`. - `Marshal(v any) ([]byte, error)`: 1. Сериализуй в utf-8 через `encoding/xml`. 2. Прогони через `charmap.Windows1251.NewEncoder()`. 3. Замени первую строку на ``. - `Unmarshal(data []byte, v any) error`: 1. Создай `xml.Decoder` с `CharsetReader`, в котором при `charset == "windows-1251"` оборачиваем reader в `charmap.Windows1251.NewDecoder().Reader(...)`. 2. Декодируй в `v`. - Проверь, что кириллица в эталонах (ФИО инвестора, наименования организаций) читается и пишется корректно. ### 6. `internal/m2m/messages_test.go` Round-trip тесты на всех эталонах. - Таблица «файл → ожидаемый тип сообщения» для всех XML в `DOC/Примеры/` и `DOC/Эталонные сообщения/`. - Для каждого файла: 1. `Unmarshal` → структура `S1`. 2. `S1.Validate()` — без ошибок. 3. `Marshal(S1)` → байты `B2`. 4. `Unmarshal(B2)` → структура `S2`. 5. `reflect.DeepEqual(S1, S2)` — true. - Юнит-тесты валидаторов на негативных кейсах (короткий ИНН, неверный префикс ReferenceId, неизвестное значение enum). - Юнит-тесты `NSDDateTime` на всех вариантах зоны. ## Требования к коду - **Без эмодзи** в коде и комментариях. - Комментарии в коде — на русском. - Имена типов и полей — на английском, как в XSD (`SettlementAccount`, `IsolationStatus`, `IIAAgreementDetails` и т. п.). - `go mod tidy` после добавления зависимостей (понадобится `golang.org/x/text/encoding/charmap`). - `make ci` (`tidy + fmt + vet + lint + test + build`) — зелёный. - Покрытие тестами `internal/m2m/` и `internal/nsdxml/` — не менее 70%. ## Коммит Один commit, сообщение: ``` feat(m2m): доменная модель сообщений + парсер windows-1251 + round-trip тесты - internal/m2m/types.go: enum'ы и simple-типы из XSD НРД (M2MSchemas_260408) - internal/m2m/validators.go: pattern-валидаторы ReferenceId/ISIN/INN/... - internal/m2m/messages.go: структуры 6 типов сообщений M2M - internal/nsdxml/datetime.go: тип NSDDateTime (формат "YYYY-MM-DDThh:mm:ss(МСК+N)") - internal/nsdxml/codec.go: Marshal/Unmarshal XML в windows-1251 - internal/m2m/messages_test.go: round-trip тесты на всех эталонах из DOC/ Co-Authored-By: Claude Opus 4.7 (1M context) ``` Push в main (история линейная, ревью через diff в Gitea постфактум). ## Проверка перед коммитом ```bash make ci ``` Если зелёное — push. Если красное — фиксируй ошибки до зелёного, не коммить промежуточные правки. ## После коммита 1. Обнови `docs/tasks/README.md`: PR-1 — «выполнено», sha коммита. 2. Обнови `internal/m2m/README.md` и `internal/nsdxml/README.md`, убери пометку «реализация — задача M1» и добавь короткое описание что есть.