Отраслевые ПП: применимость показателей + формулы ОПК
- criteria_applicability.json: Атомная (ПП-4: без 4, 10-10.7, 14; +6), Связь (ПП-402: +6, 8, 9), Оборонная (ПП-796: 1-4, 6-13.1, без 5/10/14), Транспорт (+8, 9, 13, 14), Наука (1, 7-9, 11, 13) - variables.py: _calc_criterion_8_opk (U8=(C+A+X)/Rгод*100%, ПП-796 п.18-20), _calc_criterion_13_opk (13а: V=(1-(Vmax-Vинц)/VГОЗ)*100%, 13б: T=(1-tнорм/tненорм)*100%) - ui.py: ОПК-поля в окне авторасчёта (C/A/X, Vmax/Vинц/VГОЗ, tнорм/tненорм), сохранение в criterion_input
This commit is contained in:
@@ -16,20 +16,27 @@
|
|||||||
],
|
],
|
||||||
"Наука": [
|
"Наука": [
|
||||||
"1",
|
"1",
|
||||||
"11",
|
|
||||||
"13",
|
|
||||||
"7",
|
"7",
|
||||||
"8",
|
"8",
|
||||||
"9"
|
"9",
|
||||||
|
"11",
|
||||||
|
"13"
|
||||||
],
|
],
|
||||||
"Транспорт": [
|
"Транспорт": [
|
||||||
"1",
|
"1",
|
||||||
"2",
|
"2",
|
||||||
"3",
|
"3",
|
||||||
"11"
|
"8",
|
||||||
|
"9",
|
||||||
|
"11",
|
||||||
|
"13",
|
||||||
|
"14"
|
||||||
],
|
],
|
||||||
"Связь": [
|
"Связь": [
|
||||||
"4"
|
"4",
|
||||||
|
"6",
|
||||||
|
"8",
|
||||||
|
"9"
|
||||||
],
|
],
|
||||||
"Энергетика": [
|
"Энергетика": [
|
||||||
"1",
|
"1",
|
||||||
@@ -85,20 +92,26 @@
|
|||||||
"1",
|
"1",
|
||||||
"2",
|
"2",
|
||||||
"3",
|
"3",
|
||||||
"4",
|
|
||||||
"5",
|
"5",
|
||||||
|
"6",
|
||||||
"7",
|
"7",
|
||||||
"8",
|
"8",
|
||||||
"9",
|
"9",
|
||||||
"10",
|
|
||||||
"11",
|
"11",
|
||||||
"12",
|
"12",
|
||||||
"13",
|
"13"
|
||||||
"14"
|
|
||||||
],
|
],
|
||||||
"Оборонная промышленность": [
|
"Оборонная промышленность": [
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"6",
|
||||||
|
"7",
|
||||||
"8",
|
"8",
|
||||||
"9",
|
"9",
|
||||||
|
"11",
|
||||||
|
"12",
|
||||||
"13"
|
"13"
|
||||||
],
|
],
|
||||||
"Ракетно-космическая промышленность": [
|
"Ракетно-космическая промышленность": [
|
||||||
|
|||||||
+168
-2
@@ -2207,6 +2207,13 @@ class DokoGenApp:
|
|||||||
from .variables import (CRITERION_QUANT_THRESHOLDS, CRITERION_CHOICE_INDICATORS,
|
from .variables import (CRITERION_QUANT_THRESHOLDS, CRITERION_CHOICE_INDICATORS,
|
||||||
CRITERION_QUANT_UNITS)
|
CRITERION_QUANT_UNITS)
|
||||||
|
|
||||||
|
# Сфера ОПК — для показателей 8/13а/13б используются формулы ПП РФ № 796
|
||||||
|
_sphere_opk = False
|
||||||
|
try:
|
||||||
|
_sphere_opk = (self.is_sphere_combo.get().strip() == 'Оборонная промышленность')
|
||||||
|
except Exception:
|
||||||
|
_sphere_opk = False
|
||||||
|
|
||||||
def _num(s):
|
def _num(s):
|
||||||
# «121 118 270 руб», «1 000», «906 779 руб», «2 212.5» → число
|
# «121 118 270 руб», «1 000», «906 779 руб», «2 212.5» → число
|
||||||
s = str(s or '').strip()
|
s = str(s or '').strip()
|
||||||
@@ -2247,7 +2254,54 @@ class DokoGenApp:
|
|||||||
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
|
||||||
if (num in CRITERION_QUANT_THRESHOLDS or num in CRITERION_CHOICE_INDICATORS):
|
if num in ('13а', '13б') and _sphere_opk:
|
||||||
|
# ОПК: 13а — V = (1−(V_max−V_инц)/V_ГОЗ)×100%, 13б — T = (1−(t_норм/t_ненорм))×100%
|
||||||
|
# (ПП РФ № 796 от 27.06.2026, п. 34, 38)
|
||||||
|
ttk.Label(frm, text=f"Показатель {num} — по отраслевым особенностям ОПК (ПП РФ № 796)",
|
||||||
|
font=('Arial', 10, 'bold')).pack(anchor=tk.W)
|
||||||
|
if num == '13а':
|
||||||
|
ttk.Label(frm, text="Формула: V = (1 − (V_max − V_инц) / V_ГОЗ) × 100%, где\n"
|
||||||
|
"V_max — максимальный объём продукции ГОЗ при максимальной скорости выпуска;\n"
|
||||||
|
"V_инц — объём, поставленный во время устранения компьютерного инцидента;\n"
|
||||||
|
"V_ГОЗ — полный объём продукции по ГОЗ.",
|
||||||
|
foreground='gray', wraplength=760, justify=tk.LEFT).pack(anchor=tk.W, pady=(2, 6))
|
||||||
|
|
||||||
|
def _row(label, key, default=''):
|
||||||
|
ttk.Label(frm, text=label, wraplength=900, justify=tk.LEFT).pack(anchor=tk.W, pady=(4, 0))
|
||||||
|
e = ttk.Entry(frm, width=80)
|
||||||
|
e.insert(0, str(default or ''))
|
||||||
|
e.pack(fill=tk.X)
|
||||||
|
return e
|
||||||
|
|
||||||
|
_ci13a = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||||
|
e_vmax = _row("V_max — максимальный объём продукции ГОЗ (ед.):", 'vmax', _ci13a.get('13a_vmax', ''))
|
||||||
|
e_vinc = _row("V_инц — объём продукции, поставленный во время устранения инцидента (ед.):", 'vinc', _ci13a.get('13a_vinc', ''))
|
||||||
|
e_vgoz = _row("V_ГОЗ — полный объём продукции по ГОЗ (ед.):", 'vgoz', _ci13a.get('13a_vgoz', ''))
|
||||||
|
else:
|
||||||
|
ttk.Label(frm, text="Формула: T = (1 − (t_норм / t_ненорм)) × 100%, где\n"
|
||||||
|
"t_норм — сумма времени операций по производству единицы продукции ГОЗ\n"
|
||||||
|
"при штатном функционировании объекта КИИ;\n"
|
||||||
|
"t_ненорм — сумма времени тех же операций при ненормальном функционировании.",
|
||||||
|
foreground='gray', wraplength=760, justify=tk.LEFT).pack(anchor=tk.W, pady=(2, 6))
|
||||||
|
|
||||||
|
def _row(label, key, default=''):
|
||||||
|
ttk.Label(frm, text=label, wraplength=900, justify=tk.LEFT).pack(anchor=tk.W, pady=(4, 0))
|
||||||
|
e = ttk.Entry(frm, width=80)
|
||||||
|
e.insert(0, str(default or ''))
|
||||||
|
e.pack(fill=tk.X)
|
||||||
|
return e
|
||||||
|
|
||||||
|
_ci13b = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||||
|
e_tnorm = _row("t_норм — время операций при штатном функционировании (час):", 'tnorm', _ci13b.get('13b_tnorm', ''))
|
||||||
|
e_tnon = _row("t_ненорм — время операций при ненормальном функционировании (час):", 'tnon', _ci13b.get('13b_tnonorm', ''))
|
||||||
|
result_frame = ttk.Frame(frm)
|
||||||
|
result_frame.pack(fill=tk.BOTH, expand=True, pady=(8, 0))
|
||||||
|
result = tk.Text(result_frame, height=10, wrap=tk.WORD, state=tk.NORMAL)
|
||||||
|
result_scroll = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=result.yview)
|
||||||
|
result.configure(yscrollcommand=result_scroll.set)
|
||||||
|
result.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
|
result_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
elif (num in CRITERION_QUANT_THRESHOLDS or num in CRITERION_CHOICE_INDICATORS):
|
||||||
# Остальные показатели (1–7, 11–14): количественные — ввод значения,
|
# Остальные показатели (1–7, 11–14): количественные — ввод значения,
|
||||||
# качественные — выбор варианта (III/II/I) из справочника ПП-127
|
# качественные — выбор варианта (III/II/I) из справочника ПП-127
|
||||||
from .variables import (CRITERION_QUANT_THRESHOLDS, CRITERION_CHOICE_INDICATORS,
|
from .variables import (CRITERION_QUANT_THRESHOLDS, CRITERION_CHOICE_INDICATORS,
|
||||||
@@ -2384,6 +2438,60 @@ class DokoGenApp:
|
|||||||
result.configure(yscrollcommand=result_scroll.set)
|
result.configure(yscrollcommand=result_scroll.set)
|
||||||
result.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
result.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
result_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
result_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
elif num == '8' and _sphere_opk:
|
||||||
|
# ОПК: U_8 = U_у8 / R_год × 100%, U_у8 = C + A + X (ПП РФ № 796, п. 18–20)
|
||||||
|
ttk.Label(frm, text="Показатель 8 — ущерб субъекту КИИ (отраслевые особенности ОПК, ПП РФ № 796)",
|
||||||
|
font=('Arial', 10, 'bold')).pack(anchor=tk.W)
|
||||||
|
ttk.Label(frm, text="Формула: U_8 = U_у8 / R_год × 100%, где U_у8 = C + A + X\n"
|
||||||
|
"C — суммарные затраты на объект КИИ;\n"
|
||||||
|
"A — затраты на возмещение ущерба потерпевшим (если ущерба нет, A = 0);\n"
|
||||||
|
"X — ущерб от ранее произошедших компьютерных инцидентов (если не было, X = 0);\n"
|
||||||
|
"R_год — усреднённый объём годового дохода за прошедший 5-летний период.",
|
||||||
|
foreground='gray', wraplength=760, justify=tk.LEFT).pack(anchor=tk.W, pady=(2, 6))
|
||||||
|
|
||||||
|
def _row(label, key, default=''):
|
||||||
|
ttk.Label(frm, text=label, wraplength=900, justify=tk.LEFT).pack(anchor=tk.W, pady=(4, 0))
|
||||||
|
e = ttk.Entry(frm, width=80)
|
||||||
|
e.insert(0, str(default or ''))
|
||||||
|
e.pack(fill=tk.X)
|
||||||
|
return e
|
||||||
|
|
||||||
|
_ci8 = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||||
|
_bv8 = getattr(isys, 'balance_value', '') or ''
|
||||||
|
_bvn8 = _num(_bv8)
|
||||||
|
_c_def = _num(_ci8.get('8_c', '') or '') or _bvn8
|
||||||
|
e_c = _row("C — суммарные затраты на объект КИИ (руб.):", 'c',
|
||||||
|
f"{_c_def:,.0f}" if _c_def else '')
|
||||||
|
if _bvn8 and not _num(_ci8.get('8_c', '') or ''):
|
||||||
|
ttk.Label(frm, text="⚠️ C — подставлена балансовая стоимость объекта КИИ "
|
||||||
|
f"({_bvn8:,.0f} руб.); можно поправить.",
|
||||||
|
foreground='#8a6d00', wraplength=900, justify=tk.LEFT).pack(anchor=tk.W)
|
||||||
|
e_a = _row("A — затраты на возмещение ущерба потерпевшим (руб., по умолчанию 0):", 'a',
|
||||||
|
_ci8.get('8_a', ''))
|
||||||
|
e_x = _row("X — ущерб от ранее произошедших компьютерных инцидентов (руб., по умолчанию 0):", 'x',
|
||||||
|
_ci8.get('8_x', ''))
|
||||||
|
_incs_raw8 = [getattr(self.model, f'income_year_{i}', '') or '' for i in range(1, 6)]
|
||||||
|
_incs8 = [_num(x) for x in _incs_raw8]
|
||||||
|
_incs8_f = [x for x in _incs8 if x is not None]
|
||||||
|
_inc58 = _num(getattr(self.model, 'income_5y', '') or '')
|
||||||
|
if _inc58:
|
||||||
|
_d_avg8 = _inc58 / 5.0
|
||||||
|
elif _incs8_f:
|
||||||
|
_d_avg8 = sum(_incs8_f) / len(_incs8_f)
|
||||||
|
else:
|
||||||
|
_d_avg8 = None
|
||||||
|
e_r = _row("R_год — усреднённый годовой доход за 5 лет (руб.):", 'r',
|
||||||
|
f"{_d_avg8:,.0f}" if _d_avg8 else '')
|
||||||
|
ttk.Label(frm, text="⚠️ R_год — среднее по заполненным годам; ПП-127 требует усреднение "
|
||||||
|
"за прошедший пятилетний период.",
|
||||||
|
foreground='#8a6d00', wraplength=900, justify=tk.LEFT).pack(anchor=tk.W)
|
||||||
|
result_frame = ttk.Frame(frm)
|
||||||
|
result_frame.pack(fill=tk.BOTH, expand=True, pady=(8, 0))
|
||||||
|
result = tk.Text(result_frame, height=12, wrap=tk.WORD, state=tk.NORMAL)
|
||||||
|
result_scroll = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=result.yview)
|
||||||
|
result.configure(yscrollcommand=result_scroll.set)
|
||||||
|
result.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
|
result_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
else:
|
else:
|
||||||
ttk.Label(frm, text="Показатель 8 — ущерб субъекту КИИ",
|
ttk.Label(frm, text="Показатель 8 — ущерб субъекту КИИ",
|
||||||
font=('Arial', 10, 'bold')).pack(anchor=tk.W)
|
font=('Arial', 10, 'bold')).pack(anchor=tk.W)
|
||||||
@@ -2458,7 +2566,30 @@ class DokoGenApp:
|
|||||||
|
|
||||||
def _calc():
|
def _calc():
|
||||||
lines = []
|
lines = []
|
||||||
if (num in CRITERION_QUANT_THRESHOLDS or num in CRITERION_CHOICE_INDICATORS):
|
if num in ('13а', '13б') and _sphere_opk:
|
||||||
|
from .variables import _calc_criterion_13_opk
|
||||||
|
# Сохраняем введённые составляющие формулы в criterion_input (для шаблона)
|
||||||
|
if isys:
|
||||||
|
_ci = dict(getattr(isys, 'criterion_input', None) or {})
|
||||||
|
if num == '13а':
|
||||||
|
_ci['13a_vmax'] = e_vmax.get().strip()
|
||||||
|
_ci['13a_vinc'] = e_vinc.get().strip()
|
||||||
|
_ci['13a_vgoz'] = e_vgoz.get().strip()
|
||||||
|
else:
|
||||||
|
_ci['13b_tnorm'] = e_tnorm.get().strip()
|
||||||
|
_ci['13b_tnonorm'] = e_tnon.get().strip()
|
||||||
|
isys.criterion_input = _ci
|
||||||
|
text, cat = _calc_criterion_13_opk(self.model, isys, num)
|
||||||
|
if text.startswith('Заполните'):
|
||||||
|
result.delete('1.0', tk.END)
|
||||||
|
result.insert('1.0', text)
|
||||||
|
return
|
||||||
|
lines.append(text)
|
||||||
|
if cat:
|
||||||
|
lines.append(f"Категория значимости: {cat} (по порогам ПП-127)")
|
||||||
|
else:
|
||||||
|
lines.append("Категория значимости: — (значение ниже порога III)")
|
||||||
|
elif (num in CRITERION_QUANT_THRESHOLDS or num in CRITERION_CHOICE_INDICATORS):
|
||||||
from .variables import (CRITERION_QUANT_THRESHOLDS, CRITERION_CHOICE_INDICATORS,
|
from .variables import (CRITERION_QUANT_THRESHOLDS, CRITERION_CHOICE_INDICATORS,
|
||||||
CRITERION_QUANT_UNITS, _calc_criterion_other)
|
CRITERION_QUANT_UNITS, _calc_criterion_other)
|
||||||
is_choice = num in CRITERION_CHOICE_INDICATORS
|
is_choice = num in CRITERION_CHOICE_INDICATORS
|
||||||
@@ -2508,6 +2639,41 @@ class DokoGenApp:
|
|||||||
lines.append(f"Категория значимости: {cat} (по порогам ПП-127)")
|
lines.append(f"Категория значимости: {cat} (по порогам ПП-127)")
|
||||||
else:
|
else:
|
||||||
lines.append("Категория значимости: — (значение ниже порога III)")
|
lines.append("Категория значимости: — (значение ниже порога III)")
|
||||||
|
elif num == '8' and _sphere_opk:
|
||||||
|
c = _num(e_c.get())
|
||||||
|
a = _num(e_a.get()) or 0
|
||||||
|
x = _num(e_x.get()) or 0
|
||||||
|
r = _num(e_r.get())
|
||||||
|
if c is None or r is None or r <= 0:
|
||||||
|
result.delete('1.0', tk.END)
|
||||||
|
result.insert('1.0', "Заполните C (суммарные затраты на объект) и R_год (доходы за 5 лет).")
|
||||||
|
return
|
||||||
|
if isys:
|
||||||
|
_ci = dict(getattr(isys, 'criterion_input', None) or {})
|
||||||
|
_ci['8_c'] = str(e_c.get().strip())
|
||||||
|
_ci['8_a'] = str(e_a.get().strip())
|
||||||
|
_ci['8_x'] = str(e_x.get().strip())
|
||||||
|
isys.criterion_input = _ci
|
||||||
|
u_y8 = c + a + x
|
||||||
|
val = u_y8 / r * 100
|
||||||
|
lines.append(f"Суммарные затраты на объект КИИ: C = {c:,.0f} руб.")
|
||||||
|
lines.append(f"Затраты на возмещение ущерба потерпевшим: A = {a:,.0f} руб.")
|
||||||
|
lines.append(f"Ущерб от ранее произошедших инцидентов: X = {x:,.0f} руб.")
|
||||||
|
lines.append(f"U_у8 = C + A + X = {c:,.0f} + {a:,.0f} + {x:,.0f} = {u_y8:,.0f} руб.")
|
||||||
|
lines.append(f"Усреднённый годовой доход за 5 лет: R_год = {r:,.0f} руб.")
|
||||||
|
lines.append(f"U_8 = U_у8 / R_год × 100% = {u_y8:,.0f} / {r:,.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% — ни одна категория не присваивается («—»).")
|
||||||
elif num == '8':
|
elif num == '8':
|
||||||
d = _num(e_dincome.get())
|
d = _num(e_dincome.get())
|
||||||
b = _num(e_budget.get())
|
b = _num(e_budget.get())
|
||||||
|
|||||||
+129
-1
@@ -523,6 +523,113 @@ def _calc_criterion_8_9(company, isys, num):
|
|||||||
return text, cat
|
return text, cat
|
||||||
|
|
||||||
|
|
||||||
|
def _calc_criterion_8_opk(company, isys):
|
||||||
|
"""Показатель 8 (ущерб субъекту КИИ) по отраслевым особенностям ОПК
|
||||||
|
(ПП РФ № 796 от 27.06.2026, п. 18–20):
|
||||||
|
|
||||||
|
U_8 = U_у8 / R_год × 100%,
|
||||||
|
U_у8 = C + A + X,
|
||||||
|
|
||||||
|
где C — суммарные затраты на объект КИИ;
|
||||||
|
A — затраты на возмещение ущерба потерпевшим и их имуществу (если инцидент
|
||||||
|
не влечёт ущерба, A = 0);
|
||||||
|
X — ущерб от ранее произошедших компьютерных инцидентов (если инцидентов
|
||||||
|
не было или нет данных, X = 0);
|
||||||
|
R_год — усреднённый объём годового дохода за прошедший 5-летний период.
|
||||||
|
|
||||||
|
Значения C/A/X берутся из criterion_input (ключи 8_c, 8_a, 8_x);
|
||||||
|
если C не заполнен — используется балансовая стоимость объекта КИИ.
|
||||||
|
Возвращает (текст_обоснования, категория или None).
|
||||||
|
"""
|
||||||
|
crit = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||||
|
c_val = _num(crit.get('8_c', '') or '') or _num(getattr(isys, 'balance_value', '') or '')
|
||||||
|
a_val = _num(crit.get('8_a', '') or '') or 0.0
|
||||||
|
x_val = _num(crit.get('8_x', '') or '') or 0.0
|
||||||
|
|
||||||
|
inc5 = _num(getattr(company, 'income_5y', '') or '')
|
||||||
|
if inc5:
|
||||||
|
r_avg = inc5 / 5.0
|
||||||
|
else:
|
||||||
|
incs = [_num(getattr(company, f'income_year_{i}', '') or '') for i in range(1, 6)]
|
||||||
|
incs = [x for x in incs if x is not None]
|
||||||
|
r_avg = (sum(incs) / len(incs)) if incs else None
|
||||||
|
|
||||||
|
if c_val is None or not r_avg:
|
||||||
|
return ("Показатель применим (отраслевые особенности ОПК, ПП РФ № 796). "
|
||||||
|
"Для расчёта заполните суммарные затраты на объект КИИ (C) и доходы за 5 лет "
|
||||||
|
"(или балансовую стоимость объекта КИИ).", None)
|
||||||
|
|
||||||
|
u_y8 = c_val + a_val + x_val
|
||||||
|
val_pct = u_y8 / r_avg * 100
|
||||||
|
if val_pct > 20:
|
||||||
|
cat = 'I'
|
||||||
|
elif val_pct > 10:
|
||||||
|
cat = 'II'
|
||||||
|
elif val_pct >= 1:
|
||||||
|
cat = 'III'
|
||||||
|
else:
|
||||||
|
cat = None
|
||||||
|
|
||||||
|
text = (f"Показатель 8 рассчитывается по отраслевым особенностям ОПК "
|
||||||
|
f"(ПП РФ № 796 от 27.06.2026): U_8 = U_у8 / R_год × 100%, U_у8 = C + A + X. "
|
||||||
|
f"Суммарные затраты на объект КИИ: C = {c_val:,.2f} руб. "
|
||||||
|
f"Затраты на возмещение ущерба потерпевшим: A = {a_val:,.2f} руб. "
|
||||||
|
f"Ущерб от ранее произошедших компьютерных инцидентов: X = {x_val:,.2f} руб. "
|
||||||
|
f"Ущерб субъекту КИИ: U_у8 = {c_val:,.2f} + {a_val:,.2f} + {x_val:,.2f} "
|
||||||
|
f"= {u_y8:,.2f} руб. "
|
||||||
|
f"Усреднённый годовой доход за 5-летний период: R_год = {r_avg:,.2f} руб. "
|
||||||
|
f"Показатель ущерба: U_8 = {u_y8:,.2f} / {r_avg:,.2f} × 100% = {val_pct:.6f} %.")
|
||||||
|
if cat:
|
||||||
|
text += (f" Так как {val_pct:.6f} % более 1% (пороговое значение для 3-й категории "
|
||||||
|
f"значимости), по данному показателю объекту присваивается {cat}-я категория значимости.")
|
||||||
|
else:
|
||||||
|
text += (f" Так как {val_pct:.6f} % менее 1% (пороговое значение для 3-й категории "
|
||||||
|
f"значимости), по данному показателю объекту категория значимости не присваивается.")
|
||||||
|
return text, cat
|
||||||
|
|
||||||
|
|
||||||
|
def _calc_criterion_13_opk(company, isys, num):
|
||||||
|
"""Показатели 13а/13б по отраслевым особенностям ОПК (ПП РФ № 796, п. 34, 38):
|
||||||
|
|
||||||
|
13а: V = (1 − (V_max − V_инц) / V_ГОЗ) × 100% (снижение объёма продукции ГОЗ)
|
||||||
|
13б: T = (1 − (t_норм / t_ненорм)) × 100% (увеличение времени изготовления)
|
||||||
|
|
||||||
|
Значения берутся из criterion_input: 13a_vmax, 13a_vinc, 13a_vgoz;
|
||||||
|
13b_tnorm, 13b_tnonorm. Категория — по порогам ПП-127 (CRITERION_QUANT_THRESHOLDS).
|
||||||
|
Возвращает (текст_обоснования, категория или None).
|
||||||
|
"""
|
||||||
|
crit = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||||
|
num = str(num)
|
||||||
|
if num == '13а':
|
||||||
|
vmax = _num(crit.get('13a_vmax', '') or '')
|
||||||
|
vinc = _num(crit.get('13a_vinc', '') or '')
|
||||||
|
vgoz = _num(crit.get('13a_vgoz', '') or '')
|
||||||
|
if vmax is None or vinc is None or not vgoz:
|
||||||
|
return ("Показатель 13а рассчитывается по отраслевым особенностям ОПК "
|
||||||
|
"(ПП РФ № 796): V = (1 − (V_max − V_инц) / V_ГОЗ) × 100%. "
|
||||||
|
"Заполните V_max, V_инц, V_ГОЗ (объёмы продукции ГОЗ).", None)
|
||||||
|
val = (1 - (vmax - vinc) / vgoz) * 100
|
||||||
|
formula = (f"V = (1 − ({vmax:,.2f} − {vinc:,.2f}) / {vgoz:,.2f}) × 100% = {val:.6f} %")
|
||||||
|
else: # 13б
|
||||||
|
tnorm = _num(crit.get('13b_tnorm', '') or '')
|
||||||
|
tnon = _num(crit.get('13b_tnonorm', '') or '')
|
||||||
|
if tnorm is None or tnon is None or tnon <= 0:
|
||||||
|
return ("Показатель 13б рассчитывается по отраслевым особенностям ОПК "
|
||||||
|
"(ПП РФ № 796): T = (1 − (t_норм / t_ненорм)) × 100%. "
|
||||||
|
"Заполните t_норм и t_ненорм (время операций по ГОЗ).", None)
|
||||||
|
val = (1 - tnorm / tnon) * 100
|
||||||
|
formula = (f"T = (1 − ({tnorm:,.2f} / {tnon:,.2f})) × 100% = {val:.6f} %")
|
||||||
|
|
||||||
|
cat = _criterion_other_cat(num, val)
|
||||||
|
text = (f"Показатель {num} рассчитывается по отраслевым особенностям ОПК "
|
||||||
|
f"(ПП РФ № 796 от 27.06.2026): {formula}.")
|
||||||
|
if cat:
|
||||||
|
text += (f" По порогам ПП-127 это соответствует {cat}-й категории значимости.")
|
||||||
|
else:
|
||||||
|
text += " По порогам ПП-127 категория значимости не присваивается."
|
||||||
|
return text, cat
|
||||||
|
|
||||||
|
|
||||||
# Пороги показателя 10 и подпоказателей 10.1–10.7 (ПП-127, «Значение показателя»)
|
# Пороги показателя 10 и подпоказателей 10.1–10.7 (ПП-127, «Значение показателя»)
|
||||||
CRITERION_10_THRESHOLDS = {
|
CRITERION_10_THRESHOLDS = {
|
||||||
# num: (категория → (нижняя граница, верхняя граница)), границы «включительно»
|
# num: (категория → (нижняя граница, верхняя граница)), границы «включительно»
|
||||||
@@ -800,7 +907,11 @@ def _build_categorization_indicators(company, is_list):
|
|||||||
justification = (just.get(num) or just.get(grp_num)
|
justification = (just.get(num) or just.get(grp_num)
|
||||||
or f"Показатель не применим к объекту КИИ организации сферы «{sphere or '—'}».")
|
or f"Показатель не применим к объекту КИИ организации сферы «{sphere or '—'}».")
|
||||||
elif grp_num in ('8', '9'):
|
elif grp_num in ('8', '9'):
|
||||||
text, cat = _calc_criterion_8_9(company, isys, grp_num)
|
if grp_num == '8' and sphere == 'Оборонная промышленность':
|
||||||
|
# ОПК: U_8 = (C+A+X)/R_год×100% (ПП РФ № 796, п. 18–20)
|
||||||
|
text, cat = _calc_criterion_8_opk(company, isys)
|
||||||
|
else:
|
||||||
|
text, cat = _calc_criterion_8_9(company, isys, grp_num)
|
||||||
if text:
|
if text:
|
||||||
value = text
|
value = text
|
||||||
if cat:
|
if cat:
|
||||||
@@ -815,6 +926,23 @@ def _build_categorization_indicators(company, is_list):
|
|||||||
category = f"{cat}-я категория"
|
category = f"{cat}-я категория"
|
||||||
else:
|
else:
|
||||||
category = '—'
|
category = '—'
|
||||||
|
elif num in ('13а', '13б') and sphere == 'Оборонная промышленность':
|
||||||
|
# ОПК: V = (1−(V_max−V_инц)/V_ГОЗ)×100%, T = (1−(t_норм/t_ненорм))×100%
|
||||||
|
# (ПП РФ № 796, п. 34, 38) — при наличии введённых составляющих
|
||||||
|
_opk_crit = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||||
|
_opk_ready = bool(_opk_crit.get('13a_vmax', '') or _opk_crit.get('13a_vinc', '') or _opk_crit.get('13a_vgoz', '') or _opk_crit.get('13b_tnorm', '') or _opk_crit.get('13b_tnonorm', ''))
|
||||||
|
if _opk_ready:
|
||||||
|
text, cat = _calc_criterion_13_opk(company, isys, num)
|
||||||
|
if text:
|
||||||
|
value = text
|
||||||
|
if cat:
|
||||||
|
category = f"{cat}-я категория"
|
||||||
|
else:
|
||||||
|
category = '—'
|
||||||
|
else:
|
||||||
|
value = '—'
|
||||||
|
justification = (just.get(num) or just.get(grp_num)
|
||||||
|
or 'Показатель применим к объекту КИИ; значение определяется по данным объекта (для ОПК — по формулам ПП РФ № 796: V = (1−(V_max−V_инц)/V_ГОЗ)×100%, T = (1−(t_норм/t_ненорм))×100%).')
|
||||||
else:
|
else:
|
||||||
# Применимый показатель без формульного авторасчёта (1–7, 11–14):
|
# Применимый показатель без формульного авторасчёта (1–7, 11–14):
|
||||||
# если значение введено (опросник/окно авторасчёта) — считаем категорию по порогам
|
# если значение введено (опросник/окно авторасчёта) — считаем категорию по порогам
|
||||||
|
|||||||
Reference in New Issue
Block a user