feat: авторасчёт показателей 8 и 9 (кнопка 🧮 в окне ПП 127)
- кнопка «🧮» у показателей 8 (ущерб субъекту КИИ) и 9 (ущерб бюджетам)
- п. 8: №8 = ΔДоходов / Дусредн × 100% (доходы за 5 лет), пороги ПП 127:
1-10% → III, 10-20% → II, >20% → I; применим только для госкорпораций/
ГУП/госкомпаний/стратегических АО (Указ № 1009)
- п. 9: полный расчёт по «Рекомендациям» ФСТЭК 2021:
КНП = налог/прибыль (из данных организации), ΔНП = ΔДоходов × КНП,
ΔБ = ΔНП + ΔНДПИ + ΔДИВ + ΔВСО + ΔНС, №9 = ΔБ / Бусредн × 100%,
пороги ПП 127: >0,0003-0,0006 → III, >0,0006-0,001 → II, >0,001 → I
- окно показывает все шаги расчёта, кнопка «Применить» ставит категорию
в выпадающий список и добавляет расчёт в обоснование
This commit is contained in:
+193
@@ -2151,6 +2151,13 @@ class DokoGenApp:
|
|||||||
values=options)
|
values=options)
|
||||||
combo.pack(side=tk.RIGHT, padx=6)
|
combo.pack(side=tk.RIGHT, padx=6)
|
||||||
|
|
||||||
|
# Кнопка «🧮» — авторасчёт показателя (п. 8 «ущерб субъекту КИИ» и п. 9 «ущерб бюджетам»)
|
||||||
|
if grp_num in ('8', '9'):
|
||||||
|
ttk.Button(row, text='🧮', width=3,
|
||||||
|
command=lambda n=num, nm=name, v=var, c=combo:
|
||||||
|
_open_calc_dialog(n, nm, v, c)
|
||||||
|
).pack(side=tk.RIGHT, padx=(2, 0))
|
||||||
|
|
||||||
def _on_pick(_e, _num=num, _var=var, _name=name):
|
def _on_pick(_e, _num=num, _var=var, _name=name):
|
||||||
# При выборе «не применим» — автоподстановка обоснования из словаря сферы
|
# При выборе «не применим» — автоподстановка обоснования из словаря сферы
|
||||||
if _var.get().strip() == 'не применим':
|
if _var.get().strip() == 'не применим':
|
||||||
@@ -2309,6 +2316,192 @@ class DokoGenApp:
|
|||||||
ttk.Button(btn_frame, text="💾 Сохранить", command=_save).pack(side=tk.LEFT, padx=2)
|
ttk.Button(btn_frame, text="💾 Сохранить", command=_save).pack(side=tk.LEFT, padx=2)
|
||||||
ttk.Button(btn_frame, text="Отмена", command=dialog.destroy).pack(side=tk.LEFT, padx=2)
|
ttk.Button(btn_frame, text="Отмена", command=dialog.destroy).pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
|
def _open_calc_dialog(num, name, var, combo):
|
||||||
|
"""Авторасчёт показателя 8 или 9 по «Рекомендациям по оценке показателей
|
||||||
|
критериев экономической значимости» ФСТЭК (2021). Показывает расчёт
|
||||||
|
и предлагает категорию как рекомендацию."""
|
||||||
|
import re as _re2
|
||||||
|
|
||||||
|
def _num(s):
|
||||||
|
s = str(s or '').replace(' ', '').replace('\u00a0', '')
|
||||||
|
try:
|
||||||
|
return float(s)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Данные организации (прибыль/налог) из модели
|
||||||
|
profit = getattr(self.model, 'profit', '') or ''
|
||||||
|
profit_tax = getattr(self.model, 'profit_tax', '') or ''
|
||||||
|
|
||||||
|
w = tk.Toplevel(self.root)
|
||||||
|
w.title(f"🧮 Авторасчёт показателя {num} — {name[:60]}")
|
||||||
|
w.geometry("720x560")
|
||||||
|
w.transient(self.root)
|
||||||
|
frm = ttk.Frame(w, padding=10)
|
||||||
|
frm.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
if num == '9':
|
||||||
|
ttk.Label(frm, text="Показатель 9 — ущерб бюджетам РФ (применим ко всем субъектам КИИ)",
|
||||||
|
font=('Arial', 10, 'bold')).pack(anchor=tk.W)
|
||||||
|
ttk.Label(frm, text="Формула: №9 = ΔБ / Бусредн × 100%, где ΔБ = ΔНП + ΔНДПИ + ΔДИВ + ΔВСО + ΔНС\n"
|
||||||
|
"ΔНП = ΔДоходов × (налог на прибыль / прибыль); если ΔДоходов > прибыли — весь налог.",
|
||||||
|
foreground='gray', wraplength=660, justify=tk.LEFT).pack(anchor=tk.W, pady=(2, 6))
|
||||||
|
|
||||||
|
def _row(label, key, default=''):
|
||||||
|
ttk.Label(frm, text=label).pack(anchor=tk.W, pady=(4, 0))
|
||||||
|
e = ttk.Entry(frm, width=60)
|
||||||
|
e.insert(0, str(default or ''))
|
||||||
|
e.pack(fill=tk.X)
|
||||||
|
return e
|
||||||
|
|
||||||
|
e_profit = _row("Прибыль субъекта КИИ за прошедший год (руб.):", 'profit', profit)
|
||||||
|
e_tax = _row("Налог на прибыль за прошедший год (руб.):", 'tax', profit_tax)
|
||||||
|
e_dincome = _row("Снижение уровня доходов ΔДоходов (руб., наихудший сценарий):", 'dincome', '')
|
||||||
|
e_budget = _row("Доход федерального бюджета, усреднённый за 3 года Бусредн (руб.):", 'budget',
|
||||||
|
'21228053357700')
|
||||||
|
ttk.Label(frm, text="Прочие составляющие ΔБ (если применимы, иначе 0):",
|
||||||
|
font=('', 9, 'bold')).pack(anchor=tk.W, pady=(8, 0))
|
||||||
|
e_ndpi = _row("Снижение выплат от налога на добычу ПИ ΔНДПИ (руб.):", 'ndpi', '0')
|
||||||
|
e_div = _row("Снижение выплат от дивидендов ΔДИВ (руб.):", 'div', '0')
|
||||||
|
e_vso = _row("Снижение выплат сторонних организаций ΔВСО (руб.):", 'vso', '0')
|
||||||
|
e_ns = _row("Снижение сборов ΔНС (руб.):", 'ns', '0')
|
||||||
|
result = tk.Text(frm, height=10, wrap=tk.WORD, state=tk.NORMAL)
|
||||||
|
result.pack(fill=tk.BOTH, expand=True, pady=(8, 0))
|
||||||
|
else:
|
||||||
|
ttk.Label(frm, text="Показатель 8 — ущерб субъекту КИИ",
|
||||||
|
font=('Arial', 10, 'bold')).pack(anchor=tk.W)
|
||||||
|
ttk.Label(frm, text="Применим только для: госкорпорации, ГУП, госкомпании, стратегического АО/предприятия "
|
||||||
|
"(Указ Президента № 1009).\nФормула: №8 = ΔДоходов / Дусредн × 100%.",
|
||||||
|
foreground='gray', wraplength=660, justify=tk.LEFT).pack(anchor=tk.W, pady=(2, 6))
|
||||||
|
|
||||||
|
def _row(label, key, default=''):
|
||||||
|
ttk.Label(frm, text=label).pack(anchor=tk.W, pady=(4, 0))
|
||||||
|
e = ttk.Entry(frm, width=60)
|
||||||
|
e.insert(0, str(default or ''))
|
||||||
|
e.pack(fill=tk.X)
|
||||||
|
return e
|
||||||
|
|
||||||
|
e_profit = e_tax = None
|
||||||
|
e_dincome = _row("Снижение уровня доходов ΔДоходов (руб., наихудший сценарий):", 'dincome', '')
|
||||||
|
e_budget = _row("Объём доходов субъекта КИИ, усреднённый за 5 лет Дусредн (руб.):", 'budget', '')
|
||||||
|
result = tk.Text(frm, height=10, wrap=tk.WORD, state=tk.NORMAL)
|
||||||
|
result.pack(fill=tk.BOTH, expand=True, pady=(8, 0))
|
||||||
|
|
||||||
|
def _calc():
|
||||||
|
lines = []
|
||||||
|
if num == '8':
|
||||||
|
d = _num(e_dincome.get())
|
||||||
|
b = _num(e_budget.get())
|
||||||
|
if d is None or b is None or b <= 0:
|
||||||
|
result.delete('1.0', tk.END)
|
||||||
|
result.insert('1.0', "Заполните оба поля (ΔДоходов и Дусредн).")
|
||||||
|
return
|
||||||
|
val = d / b * 100
|
||||||
|
lines.append(f"Снижение уровня доходов ΔДоходов: {d:,.0f} руб.")
|
||||||
|
lines.append(f"Объём доходов, усреднённый за 5 лет (Дусредн): {b:,.0f} руб.")
|
||||||
|
lines.append(f"№8 = ΔДоходов / Дусредн × 100% = {d:,.0f} / {b:,.0f} × 100 = {val:.2f} %")
|
||||||
|
if val > 20:
|
||||||
|
cat = 'I'
|
||||||
|
elif val > 10:
|
||||||
|
cat = 'II'
|
||||||
|
elif val >= 1:
|
||||||
|
cat = 'III'
|
||||||
|
else:
|
||||||
|
cat = None
|
||||||
|
if cat:
|
||||||
|
lines.append(f"Категория значимости: {cat} (более 20% → I; 10-20% → II; 1-10% → III)")
|
||||||
|
else:
|
||||||
|
lines.append("Значение менее 1% — ни одна категория не присваивается («—»).")
|
||||||
|
else:
|
||||||
|
p = _num(e_profit.get())
|
||||||
|
t = _num(e_tax.get())
|
||||||
|
d = _num(e_dincome.get())
|
||||||
|
b = _num(e_budget.get())
|
||||||
|
if d is None or b is None or b <= 0:
|
||||||
|
result.delete('1.0', tk.END)
|
||||||
|
result.insert('1.0', "Заполните ΔДоходов и Бусредн (прибыль/налог — из данных организации).")
|
||||||
|
return
|
||||||
|
if p and t is not None and p > 0:
|
||||||
|
knp = t / p
|
||||||
|
dnp = d * knp
|
||||||
|
if d > p:
|
||||||
|
dnp = t
|
||||||
|
lines.append(f"Коэффициент КНП = налог на прибыль / прибыль = {t:,.0f} / {p:,.0f} = {knp:.4f}")
|
||||||
|
lines.append(f"ΔНП = ΔДоходов × КНП = {d:,.0f} × {knp:.4f} = {dnp:,.1f} руб."
|
||||||
|
+ (" (ΔДоходов > прибыли → весь налог)" if d > p else ""))
|
||||||
|
else:
|
||||||
|
dnp = 0
|
||||||
|
lines.append("Прибыль/налог не заполнены — ΔНП принят равным 0 (расчёт неполный).")
|
||||||
|
ndpi = _num(e_ndpi.get()) or 0
|
||||||
|
div = _num(e_div.get()) or 0
|
||||||
|
vso = _num(e_vso.get()) or 0
|
||||||
|
ns = _num(e_ns.get()) or 0
|
||||||
|
db = dnp + ndpi + div + vso + ns
|
||||||
|
lines.append(f"ΔНДПИ = {ndpi:,.0f} руб., ΔДИВ = {div:,.0f} руб., ΔВСО = {vso:,.0f} руб., ΔНС = {ns:,.0f} руб.")
|
||||||
|
lines.append(f"ΔБ = ΔНП + ΔНДПИ + ΔДИВ + ΔВСО + ΔНС = {db:,.1f} руб.")
|
||||||
|
lines.append(f"Бусредн (доход федерального бюджета за 3 года): {b:,.0f} руб.")
|
||||||
|
val = db / b * 100
|
||||||
|
lines.append(f"№9 = ΔБ / Бусредн × 100% = {db:,.1f} / {b:,.0f} × 100 = {val:.6f} %")
|
||||||
|
if val > 0.001:
|
||||||
|
cat = 'I'
|
||||||
|
elif val > 0.0006:
|
||||||
|
cat = 'II'
|
||||||
|
elif val > 0.0003:
|
||||||
|
cat = 'III'
|
||||||
|
else:
|
||||||
|
cat = None
|
||||||
|
if cat:
|
||||||
|
lines.append(f"Категория значимости: {cat} (более 0,001% → I; 0,0006-0,001% → II; 0,0003-0,0006% → III)")
|
||||||
|
else:
|
||||||
|
lines.append("Значение ≤ 0,0003% — ни одна категория не присваивается («—»).")
|
||||||
|
result.delete('1.0', tk.END)
|
||||||
|
result.insert('1.0', '\n'.join(lines))
|
||||||
|
|
||||||
|
def _apply():
|
||||||
|
_calc()
|
||||||
|
txt = result.get('1.0', 'end-1c').strip()
|
||||||
|
lines = txt.split('\n')
|
||||||
|
cat_line = next((l for l in lines if l.startswith('Категория значимости:')), '')
|
||||||
|
cat = None
|
||||||
|
if 'Категория значимости: I' in cat_line:
|
||||||
|
cat = 'I'
|
||||||
|
elif 'Категория значимости: II' in cat_line:
|
||||||
|
cat = 'II'
|
||||||
|
elif 'Категория значимости: III' in cat_line:
|
||||||
|
cat = 'III'
|
||||||
|
if cat:
|
||||||
|
# Выбрать в комбобоксе соответствующую категорию
|
||||||
|
opts = list(combo['values'])
|
||||||
|
target = next((o for o in opts if o.startswith(f"{cat} категория")), None)
|
||||||
|
if target:
|
||||||
|
var.set(target)
|
||||||
|
# Обоснование с расчётом
|
||||||
|
prefix = f"Показатель {num}. {name} — {cat} категория."
|
||||||
|
full = prefix + "\n" + txt
|
||||||
|
cur = justify_txt.get('1.0', 'end-1c').strip()
|
||||||
|
if prefix not in cur:
|
||||||
|
if cur:
|
||||||
|
justify_txt.insert('end', '\n' + full)
|
||||||
|
else:
|
||||||
|
justify_txt.insert('1.0', full)
|
||||||
|
else:
|
||||||
|
var.set('—')
|
||||||
|
prefix = f"Показатель {num}. {name} — применим, категория не присвоена (—)."
|
||||||
|
full = prefix + "\n" + txt
|
||||||
|
cur = justify_txt.get('1.0', 'end-1c').strip()
|
||||||
|
if prefix not in cur:
|
||||||
|
if cur:
|
||||||
|
justify_txt.insert('end', '\n' + full)
|
||||||
|
else:
|
||||||
|
justify_txt.insert('1.0', full)
|
||||||
|
w.destroy()
|
||||||
|
|
||||||
|
btns = ttk.Frame(frm)
|
||||||
|
btns.pack(fill=tk.X, pady=(8, 0))
|
||||||
|
ttk.Button(btns, text="🧮 Рассчитать", command=_calc).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(btns, text="✔️ Применить категорию", command=_apply).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(btns, text="Отмена", command=w.destroy).pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
dialog.wait_window()
|
dialog.wait_window()
|
||||||
|
|
||||||
def _download_fstec_threats(self):
|
def _download_fstec_threats(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user