arm-ka-docs/scripts/mockup_crawl_extra.py
Andrey Epifancev 347566a00a docs: сверка макета со снимками — 22 новых снимка, mockup_crawl_extra.py, OQ-31…OQ-47
10-user-journey.md и mockup/INDEX.md сверены с 33 снимками; досняты Playwright модалка
«Изменить статус», формы ГК/ЮЛ/ГСК/ГСЗ, меню ЮЛ и ГСК, раскрытый срез ЕХ, режимы редактирования
вкладок отчёта, экран 03. Раздел «Что осталось уточнить» закрыт (экрана проверки ОСВ в макете
нет). Расхождения макета с DBML/правилами — OQ-31…OQ-44; OQ-45…OQ-47 — по контракту и seed
Среза 0; OQ-29 дополнен ролями макета.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 02:44:14 +04:00

247 lines
15 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Досъёмка экранов React-макета dkb_24.09.26, которые не поймал mockup_crawl.py → PNG + report-extra.json.
python scripts/mockup_crawl_extra.py docs/mockup/dkb_24.09.26/dkb_24.09.26.html docs/mockup/screens
Зависимости: pip install playwright && playwright install chromium
Что снимает (имена продолжают нумерацию INDEX.md):
03-report-groups Отчётность → Группы компаний (таблица ГК с кнопкой «Консолидации»)
33-01-change-status-modal модалка «Изменить статус» на консолидации в работе (2026 К2, «Консолидация РФ»)
43-01-add-version меню «…» консолидации → «Добавить версию»
43-02-consolidation-history меню «…» консолидации → «История изменений»
39-01-edit-documents панель «Первичные документы» → «Редактировать документы»
41-01-document-check-osv клик по строке ОСВ (xlsx) в панели документов — в макете строка readonly, снимок не создаётся
41-02-document-check-edit «Проверка распознавания» PDF-документа → «Редактировать»
51-01…51-04 формы «Создать ГК» / «Добавить ЮЛ» / «Создать ГСК» / «Создать ГСЗ»
77-01-gsk-row-menu, 77-02-edit-gsk меню «…» строки ГСК и форма «Изменить ГСК»
79-le-edit, 79-01-le-move-gk, 79-02-le-history «Изменить ЮЛ», «Перевести в другую ГК», «История изменений»
83-01-ex-snapshot Единое хранилище → раскрытая «Консолидация 2024 К1» (сформированная отчётность + первичка)
31-02-01-corrections-edit, 31-03-01-goodwill-edit, 31-04-01-kfo-edit-comments, 31-05-01-kfo-periods
31-02-02-corrections-wide, 31-01-01-chess-wide те же вкладки при viewport 2800px (все колонки в кадре)
"""
import asyncio
import json
import os
import sys
from playwright.async_api import async_playwright
URL = "file://" + os.path.abspath(sys.argv[1])
OUT = sys.argv[2]
REPORT = {}
async def click_text(pg, text, tag=None, nth=0, exact=False):
ok = await pg.evaluate(
"""([text, tag, nth, exact]) => {
const els=[...document.querySelectorAll(tag || 'button, a, [role=tab], [role=menuitem], tr, li, div, span, td')]
.filter(e => { const t=(e.innerText||'').trim().replace(/\\s+/g,' ');
return (exact ? t===text : t.startsWith(text)) && e.getBoundingClientRect().width>5; });
const e = els[nth]; if(!e) return false; e.scrollIntoView({block:'center'}); e.click(); return true; }""",
[text, tag, nth, exact],
)
await pg.wait_for_timeout(700)
return ok
async def click_row_menu(pg, row_index=0, table_index=0):
"""Клик по последней кнопке («…») в строке таблицы."""
ok = await pg.evaluate(
"""([ri, ti]) => { const tables=[...document.querySelectorAll('table')]; const t=tables[ti]; if(!t) return 'notable';
const r=[...t.querySelectorAll('tbody tr')][ri]; if(!r) return 'norow'; const bs=[...r.querySelectorAll('button')];
const b=bs[bs.length-1]; if(!b) return 'nobtn'; b.click(); return true; }""",
[row_index, table_index],
)
await pg.wait_for_timeout(600)
return ok
async def snap(pg, name, note="", full_page=False):
await pg.screenshot(path=f"{OUT}/{name}.png", full_page=full_page)
d = await pg.evaluate(
"""() => { const q=s=>[...document.querySelectorAll(s)]; const t=e=>(e.innerText||'').trim().replace(/\\s+/g,' ');
const modal = q('[class*=modal],[class*=Modal],[role=dialog],[class*=drawer],[class*=Drawer],[class*=overlay],[class*=Overlay]')
.filter(e=>e.getBoundingClientRect().width>100).map(t).slice(0,1).map(x=>x.slice(0,1500));
const inputs = q('input,textarea,select').filter(e=>e.getBoundingClientRect().width>5)
.map(e=>(e.tagName+':'+(e.type||'')+':'+(e.placeholder||'')+':'+(e.value||'').slice(0,40)));
return {heads:q('h1,h2,h3,h4').map(t).filter(Boolean).slice(0,12),
buttons:[...new Set(q('button').map(t).filter(x=>x&&x.length<60&&!x.includes('CRM ID')&&!x.endsWith('ГСК')))],
labels:[...new Set(q('label').map(t).filter(x=>x&&x.length<80))].slice(0,40), inputs,
cols:[...new Set(q('th').map(t).filter(Boolean))].slice(0,60), modal, body:t(document.body).slice(0,3000)} }"""
)
d["note"] = note
REPORT[name] = d
print(f"## {name}: heads={d['heads'][:4]} modal={bool(d['modal'])} labels={d['labels'][:12]}")
return d
async def fresh(pg):
await pg.goto(URL)
await pg.evaluate("() => { localStorage.clear(); sessionStorage.clear(); }")
await pg.goto(URL)
await pg.wait_for_timeout(1600)
async def dismiss_inn_warning(pg):
await click_text(pg, "Перейти", "button", 0, True)
async def goto_consolidations(pg):
await fresh(pg)
await click_text(pg, "Группы компаний", "button")
await click_text(pg, "Консолидации", "button")
async def step(name, coro):
try:
await coro
except Exception as exc: # noqa: BLE001 — съёмка не должна падать целиком из-за одного экрана
print(f"!! {name}: {type(exc).__name__}: {exc}")
REPORT.setdefault("_errors", []).append(f"{name}: {exc}")
async def main():
async with async_playwright() as p:
b = await p.chromium.launch()
pg = await b.new_page(viewport={"width": 1600, "height": 1000})
async def s03():
await fresh(pg)
await click_text(pg, "Группы компаний", "button")
await snap(pg, "03-report-groups", "Отчётность → Группы компаний")
async def s33():
await goto_consolidations(pg)
await click_text(pg, "К отчетности", "button", 3) # 2026 К2 — «Консолидация РФ»
await click_text(pg, "Изменить статус", "button")
await snap(pg, "33-01-change-status-modal", "модалка «Изменить статус» на консолидации в работе")
async def s43():
await goto_consolidations(pg)
await click_row_menu(pg, 0, 0)
await click_text(pg, "Добавить версию", None, 0, True)
await snap(pg, "43-01-add-version", "меню «…» → Добавить версию")
await goto_consolidations(pg)
await click_row_menu(pg, 0, 0)
await click_text(pg, "История изменений", None, 0, True)
await snap(pg, "43-02-consolidation-history", "меню «…» → История изменений")
async def s39_41():
await goto_consolidations(pg)
await click_text(pg, "2024", "td", 0, False) # выбрать консолидацию → панель «Организации»
await click_text(pg, "Документы", "button")
await click_text(pg, "Редактировать документы", "button")
await snap(pg, "39-01-edit-documents", "панель документов → Редактировать документы")
await goto_consolidations(pg)
await click_text(pg, "2024", "td", 0, False)
await click_text(pg, "Документы", "button")
# Строки ОСВ (xlsx) в панели документов имеют класс doc-row--readonly — клик ничего не открывает,
# «Проверка распознавания» в макете есть только для PDF (doc-row--previewable). Снимок делаем,
# только если экран действительно открылся.
cls = await pg.evaluate(
"""() => { const els=[...document.querySelectorAll('.doc-row')].filter(e=>(e.innerText||'').includes('ОСВ'));
const e=els[0]; if(!e) return null; e.click(); return e.className; }"""
)
await pg.wait_for_timeout(900)
body = await pg.evaluate("() => (document.body.innerText||'').replace(/\\s+/g,' ')")
if "Проверка распознавания" in body:
await snap(pg, "41-01-document-check-osv", f"клик по строке ОСВ в панели документов ({cls})")
else:
REPORT["41-01-document-check-osv"] = {"note": f"не снято: строка ОСВ не открывает экран проверки (класс строки: {cls})"}
print(f"## 41-01-document-check-osv: не снято — строка ОСВ readonly ({cls})")
# PDF-документ (doc-row--previewable) → «Проверка распознавания» → «Редактировать»
await goto_consolidations(pg)
await click_text(pg, "2024", "td", 0, False)
await click_text(pg, "Документы", "button")
await pg.evaluate(
"""() => { const els=[...document.querySelectorAll('.doc-row--previewable')].filter(e=>(e.innerText||'').includes('ОФР'));
const e=els[0]; if(e) e.click(); }"""
)
await pg.wait_for_timeout(1200)
if await click_text(pg, "Редактировать", "button", 0, True):
await snap(pg, "41-02-document-check-edit", "Проверка распознавания (PDF) → Редактировать")
async def s_wide():
"""Широкий viewport: таблицы корректировки и шахматки целиком (в 1600px колонки «Контроль»/«Корректировка» за кадром)."""
wide = await b.new_page(viewport={"width": 2800, "height": 1100})
try:
await goto_consolidations(wide)
await click_text(wide, "К отчетности", "button")
await click_text(wide, "Таблица корректировки", "button", 0, True)
await dismiss_inn_warning(wide)
await wide.evaluate(
"""() => { const td=[...document.querySelectorAll('table tbody tr td')][0]; const b=td && td.querySelector('button, svg, span'); if(b) b.click(); }"""
)
await wide.wait_for_timeout(600)
await snap(wide, "31-02-02-corrections-wide", "Таблица корректировки, viewport 2800px, первая пара раскрыта по субсчетам")
await click_text(wide, "Шахматка", "button", 0, True)
await snap(wide, "31-01-01-chess-wide", "Шахматка, viewport 2800px")
finally:
await wide.close()
async def s51():
for obj, name in [
("ГК Группа компаний", "51-01-create-gk-form"),
("ЮЛ Юридическое лицо", "51-02-create-le-form"),
("ГСК Группа связанных компаний", "51-03-create-gsk-form"),
("ГСЗ Группа связанных заемщиков", "51-04-create-gsz-form"),
]:
await fresh(pg)
await click_text(pg, "Создать ГК/ЮЛ", "button")
await click_text(pg, obj, "button", 0, True)
await snap(pg, name, f"Создать ГК/ЮЛ → {obj}")
async def s77():
await fresh(pg)
await click_text(pg, "ГСК", "button", 0, False)
await click_row_menu(pg, 0, 0)
await snap(pg, "77-01-gsk-row-menu", "меню «…» строки ГСК")
await click_text(pg, "Изменить ГСК", None, 0, True)
await snap(pg, "77-02-edit-gsk", "Изменить ГСК")
async def s79():
await fresh(pg)
await click_row_menu(pg, 0, 0)
await click_text(pg, "Изменить ЮЛ", None, 0, True)
await snap(pg, "79-le-edit", "меню ЮЛ → Изменить ЮЛ")
await fresh(pg)
await click_row_menu(pg, 0, 0)
await click_text(pg, "Перевести в другую ГК", None, 0, True)
await snap(pg, "79-01-le-move-gk", "меню ЮЛ → Перевести в другую ГК")
await fresh(pg)
await click_row_menu(pg, 0, 0)
await click_text(pg, "История изменений", None, 0, True)
await snap(pg, "79-02-le-history", "меню ЮЛ → История изменений")
async def s83():
await fresh(pg)
await click_text(pg, "Единое хранилище", "button")
await click_text(pg, "Консолидация 2024 К1", None, 0, False)
await snap(pg, "83-01-ex-snapshot", "ЕХ → раскрыта Консолидация 2024 К1 (верифицирована)", full_page=True)
async def s31():
await goto_consolidations(pg)
await click_text(pg, "К отчетности", "button")
await click_text(pg, "Таблица корректировки", "button", 0, True)
await dismiss_inn_warning(pg)
await click_text(pg, "Редактировать", "button", 0, True)
await snap(pg, "31-02-01-corrections-edit", "Таблица корректировки → Редактировать")
await click_text(pg, "Гудвилл", "button", 0, True)
await click_text(pg, "Редактировать", "button", 0, True)
await snap(pg, "31-03-01-goodwill-edit", "Гудвилл → Редактировать")
await click_text(pg, "КФО", "button", 0, True)
await click_text(pg, "Редактировать комментарии", "button", 0, True)
await snap(pg, "31-04-01-kfo-edit-comments", "КФО → Редактировать комментарии")
await click_text(pg, "КФО Общий", "button", 0, True)
await click_text(pg, "Выбор периодов", "button", 0, True)
await snap(pg, "31-05-01-kfo-periods", "КФО Общий → Выбор периодов")
for name, fn in [("03", s03), ("33", s33), ("43", s43), ("39/41", s39_41), ("51", s51), ("77", s77), ("79", s79), ("83", s83), ("31", s31), ("wide", s_wide)]:
await step(name, fn())
json.dump(REPORT, open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "mockup", "report-extra.json"), "w"), ensure_ascii=False, indent=1)
await b.close()
asyncio.run(main())