# G1/SP3c — полные платёжные реквизиты (фронт) — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Расширить вкладку «Реквизиты» блоком полных платёжных реквизитов (КПП/ОГРН/юр.адрес/банк) с видимостью полей по типу лица и чипом-статусом «Готово к оплате».

**Architecture:** Правка одного компонента `RequisitesTab.vue` (SP3b): +computed-флаги видимости по `subject_type`, +валидация платёжных форматов в существующем `validateClient()`, +блок «Реквизиты для оплаты» в шаблоне со статус-чипом. Бэкенд, api-слой, роуты — без изменений.

**Tech Stack:** Vue 3 `<script setup lang="ts">` + Vuetify 3.

> **⚠ Тестовый раннер:** vitest на фронте сломан (G8). TDD-цикл заменён на `vue-tsc` + eslint + **живой Playwright** (Task 3, обязательно). Сознательное отступление, как в SP3b.

**Spec:** [docs/superpowers/specs/2026-06-19-g1-sp3c-full-requisites-frontend-design.md](../specs/2026-06-19-g1-sp3c-full-requisites-frontend-design.md)

---

## File Structure

| Файл | Ответственность |
|---|---|
| `app/resources/js/views/settings/RequisitesTab.vue` | **правка** — +computed видимости, +валидация платёжных полей, +блок платёжных реквизитов со статус-чипом |

`api/requisites.ts` уже содержит все поля в `Requisites` и шлёт всю форму в `updateRequisites` — **не трогаем**.

---

## Task 1: Computed-флаги видимости + валидация платёжных полей (script)

**Files:**
- Modify: `app/resources/js/views/settings/RequisitesTab.vue` (блок `<script setup>`)

- [ ] **Step 1: Добавить computed-флаги видимости по типу лица**

После строки `const requiresInn = computed(` … `);` (она заканчивается `);`) добавить:

```ts
const isLegalEntity = computed(() => form.subject_type === 'legal_entity');
const isSoleProprietor = computed(() => form.subject_type === 'sole_proprietor');

// Блок платёжных реквизитов виден, как только выбран тип лица.
const showPayment = computed(() => form.subject_type !== null);
// КПП — только юрлицо; ОГРН/ОГРНИП и юр.адрес — юрлицо и ИП; банк — всегда (когда showPayment).
const showKpp = computed(() => isLegalEntity.value);
const showOgrn = computed(() => isLegalEntity.value || isSoleProprietor.value);
const showLegalAddress = computed(() => isLegalEntity.value || isSoleProprietor.value);
const ogrnLabel = computed(() => (isSoleProprietor.value ? 'ОГРНИП' : 'ОГРН'));

const isCompleted = computed(() => Boolean(form.requisites_completed_at));
```

- [ ] **Step 2: Расширить validateClient платёжными форматами**

Заменить тело функции `validateClient()` на (добавлены платёжные проверки в конце,
перед `errors.value = e;`):

```ts
function validateClient(): boolean {
    const e: Record<string, string[]> = {};
    if (!form.subject_type) e.subject_type = ['Выберите тип лица'];
    if (!form.contact_name || form.contact_name.trim() === '') e.contact_name = ['Укажите контактное имя'];
    if (!form.contact_phone || form.contact_phone.trim() === '') e.contact_phone = ['Укажите телефон'];
    if (requiresInn.value && (!form.inn || form.inn.trim() === '')) e.inn = ['ИНН обязателен для ИП и юрлица'];

    // Платёжные поля — необязательные; проверяем формат только если заполнены.
    const kpp = form.kpp?.trim() ?? '';
    if (kpp !== '' && !/^\d{9}$/.test(kpp)) e.kpp = ['КПП — 9 цифр'];

    const ogrn = form.ogrn?.trim() ?? '';
    if (ogrn !== '') {
        const len = isSoleProprietor.value ? 15 : 13;
        if (!new RegExp(`^\\d{${len}}$`).test(ogrn)) {
            e.ogrn = [isSoleProprietor.value ? 'ОГРНИП — 15 цифр' : 'ОГРН — 13 цифр'];
        }
    }

    const bik = form.bank_bik?.trim() ?? '';
    if (bik !== '' && !/^\d{9}$/.test(bik)) e.bank_bik = ['БИК — 9 цифр'];

    const acc = form.bank_account?.trim() ?? '';
    if (acc !== '' && !/^\d{20}$/.test(acc)) e.bank_account = ['Расчётный счёт — 20 цифр'];

    const corr = form.corr_account?.trim() ?? '';
    if (corr !== '' && !/^\d{20}$/.test(corr)) e.corr_account = ['Корр. счёт — 20 цифр'];

    errors.value = e;
    return Object.keys(e).length === 0;
}
```

- [ ] **Step 3: Type-check + eslint**

Run: `npm --prefix app run type-check`
Expected: exit 0 (новые computed пока не используются в шаблоне — допустимо до Task 2; vue-tsc на неиспользуемые const в `<script setup>` не ругается).
Run: `npx --prefix app eslint resources/js/views/settings/RequisitesTab.vue`
Expected: 0 ошибок (eslint может предупредить о неиспользуемых — если так, это снимется в Task 2; при ошибке `no-unused-vars` выполнить Task 2 до коммита и закоммитить вместе).

- [ ] **Step 4: Commit (если eslint чист)**

```bash
git add app/resources/js/views/settings/RequisitesTab.vue
LEFTHOOK=0 git commit -m "feat(G1/SP3c): computed видимости платёжных полей + валидация форматов

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"
```

> Если eslint в Step 3 ругается на неиспользуемые computed — пропустить Step 4, сделать Task 2, затем единый коммит в Task 2 Step 3.

---

## Task 2: Блок платёжных реквизитов в шаблоне

**Files:**
- Modify: `app/resources/js/views/settings/RequisitesTab.vue` (`<template>`)

- [ ] **Step 1: Вставить блок платёжных реквизитов**

В шаблоне найти закрывающий тег `</v-row>` лёгкой формы (тот, что идёт перед
блоком `<div class="d-flex ga-2 mt-4">` с кнопкой «Сохранить») и **между** этим
`</v-row>` и `<div class="d-flex ga-2 mt-4">` вставить:

```html
        <template v-if="showPayment">
            <v-divider class="my-5" />

            <div class="d-flex align-center ga-3 mb-1">
                <h3 class="text-subtitle-1 ma-0">Реквизиты для оплаты</h3>
                <v-chip
                    :color="isCompleted ? 'success' : undefined"
                    size="small"
                    variant="tonal"
                    data-testid="requisites-payment-status"
                >
                    {{ isCompleted ? 'Готово к оплате' : 'Не заполнено' }}
                </v-chip>
            </div>
            <p class="text-body-2 text-medium-emphasis mb-4">
                Нужны для выставления счёта и договора. Можно заполнить позже.
            </p>

            <v-row dense>
                <v-col v-if="showKpp" cols="12" md="6">
                    <v-text-field
                        v-model="form.kpp"
                        label="КПП"
                        inputmode="numeric"
                        variant="outlined"
                        density="comfortable"
                        :error-messages="errors.kpp"
                        data-testid="requisites-kpp"
                    />
                </v-col>
                <v-col v-if="showOgrn" cols="12" md="6">
                    <v-text-field
                        v-model="form.ogrn"
                        :label="ogrnLabel"
                        inputmode="numeric"
                        variant="outlined"
                        density="comfortable"
                        :error-messages="errors.ogrn"
                        data-testid="requisites-ogrn"
                    />
                </v-col>
                <v-col v-if="showLegalAddress" cols="12">
                    <v-text-field
                        v-model="form.legal_address"
                        label="Юридический адрес"
                        variant="outlined"
                        density="comfortable"
                        :error-messages="errors.legal_address"
                        data-testid="requisites-legal-address"
                    />
                </v-col>

                <v-col cols="12" md="6">
                    <v-text-field
                        v-model="form.bank_name"
                        label="Наименование банка"
                        variant="outlined"
                        density="comfortable"
                        :error-messages="errors.bank_name"
                        data-testid="requisites-bank-name"
                    />
                </v-col>
                <v-col cols="12" md="6">
                    <v-text-field
                        v-model="form.bank_bik"
                        label="БИК"
                        inputmode="numeric"
                        variant="outlined"
                        density="comfortable"
                        :error-messages="errors.bank_bik"
                        data-testid="requisites-bank-bik"
                    />
                </v-col>
                <v-col cols="12" md="6">
                    <v-text-field
                        v-model="form.bank_account"
                        label="Расчётный счёт"
                        inputmode="numeric"
                        variant="outlined"
                        density="comfortable"
                        :error-messages="errors.bank_account"
                        data-testid="requisites-bank-account"
                    />
                </v-col>
                <v-col cols="12" md="6">
                    <v-text-field
                        v-model="form.corr_account"
                        label="Корреспондентский счёт"
                        inputmode="numeric"
                        variant="outlined"
                        density="comfortable"
                        :error-messages="errors.corr_account"
                        data-testid="requisites-corr-account"
                    />
                </v-col>
            </v-row>
        </template>
```

- [ ] **Step 2: Type-check + eslint**

Run: `npm --prefix app run type-check`
Expected: exit 0.
Run: `npx --prefix app eslint resources/js/views/settings/RequisitesTab.vue`
Expected: 0 ошибок.

- [ ] **Step 3: Commit**

```bash
git add app/resources/js/views/settings/RequisitesTab.vue
LEFTHOOK=0 git commit -m "feat(G1/SP3c): блок платёжных реквизитов + статус-чип (поля по типу лица)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"
```

> Если Task 1 Step 4 был пропущен (eslint ругался на неиспользуемые) — этот коммит включит и script-, и template-правки одним коммитом.

---

## Task 3: Сборка + статпроверка + живой Playwright

**Files:** (нет правок — только проверки)

**Предусловие:** backend на `127.0.0.1:8000`, ассеты пересобраны. Используем клиента
из SP3b (`sp3b-test-0619@example.org` / `password123`), у него лёгкие реквизиты
(Физлицо) уже сохранены.

- [ ] **Step 1: Полный type-check + eslint + build**

Run: `npm --prefix app run type-check` → exit 0.
Run: `npx --prefix app eslint resources/js/views/settings/RequisitesTab.vue` → 0 ошибок.
Run: `npm --prefix app run build` → успех.

- [ ] **Step 2: Вход клиентом из SP3b**

Через Playwright MCP:
1. `browser_navigate` → `http://127.0.0.1:8000/login`.
2. Войти `sp3b-test-0619@example.org` / `password123`.
3. `browser_navigate` → `/settings?tab=requisites`.
Expected: форма предзаполнена (Физлицо, Иван Петров). При Физлице платёжный блок
показывает только банковские поля; чип `[data-testid="requisites-payment-status"]`
= «Не заполнено».

- [ ] **Step 3: ИП — видимость полей + заполнение + чип**

1. Кликнуть «ИП» (`[data-testid="requisites-subject-type"]`).
Expected: видны `[data-testid="requisites-ogrn"]` (лейбл «ОГРНИП»),
`[data-testid="requisites-legal-address"]`, банковские; КПП
(`[data-testid="requisites-kpp"]`) **отсутствует**.
2. Заполнить: ИНН валидный 12-значный ИП (напр. `500100732259`), ОГРНИП `304500116000157`
(15 цифр), БИК `044525225`, Р/с `40702810500000000001`, К/с `30101810400000000225`.
3. Кликнуть «Сохранить».
Expected: `[data-testid="requisites-save-success"]` появился; чип стал
«Готово к оплате».

- [ ] **Step 4: Переключение типа — видимость КПП / скрытие для физлица**

1. Кликнуть «Юрлицо».
Expected: появился `[data-testid="requisites-kpp"]` (лейбл ОГРН без «ИП»).
2. Кликнуть «Физлицо».
Expected: `[data-testid="requisites-kpp"]`, `[data-testid="requisites-ogrn"]`,
`[data-testid="requisites-legal-address"]` **отсутствуют**; банковские поля видны.

- [ ] **Step 5: Негатив — БИК 8 цифр**

1. Выбрать «Юрлицо», ввести БИК `04452522` (8 цифр), Сохранить.
Expected: ошибка под `[data-testid="requisites-bank-bik"]` «БИК — 9 цифр».

- [ ] **Step 6: Зафиксировать результат**

`browser_take_screenshot` вкладки с чипом «Готово к оплате». Отчёт — в ответе владельцу.

---

## Self-Review (выполнено автором плана)

**1. Spec coverage:**
- §3.1 блок + чип-статус → Task 2 Step 1 ✓
- §3.2 поля по типу лица → Task 1 (computed) + Task 2 (v-if) ✓
- §3.3 валидация платёжных форматов → Task 1 Step 2 ✓
- §3.4 GET/PUT/обновление чипа (через SP3b механизмы) → не требует кода, проверяется в Task 3 ✓
- §5 верификация → Task 3 ✓
- §6 YAGNI (без бэкенда/api/гейта оплаты) → соблюдено ✓

**2. Placeholder scan:** плейсхолдеров нет; весь код приведён целиком.

**3. Type consistency:** computed `isSoleProprietor` определён в Task 1 Step 1 и
используется в Task 1 Step 2 (`validateClient`) тем же именем; `showKpp/showOgrn/
showLegalAddress/showPayment/ogrnLabel/isCompleted` определены в Task 1 и
используются в Task 2 шаблоне теми же именами; поля формы (`kpp/ogrn/legal_address/
bank_name/bank_bik/bank_account/corr_account`) — из `Requisites` (SP3b). data-testid
в Task 2 совпадают с селекторами Task 3.
