From 5bb6a2f5e9045ec98b150d83461c87d51fe6651e Mon Sep 17 00:00:00 2001 From: LocoAgent Date: Wed, 5 Aug 2026 14:54:31 +0400 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA=D0=B0=20?= =?UTF-8?q?=C2=AB=D0=9E=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=C2=BB:=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=205?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9=20=D0=B4=D0=BE=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=D0=BE=D0=B2=20=D0=BF=D0=BE=20=D0=B3=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=D0=BC=20=E2=80=94=20=D0=BE=D0=B4=D0=BD=D0=B0=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D0=B0=20=C2=AB=D0=94=D0=BE=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=20=D0=B7=D0=B0=205=20=D0=BB=D0=B5=D1=82=20(=D1=81=D1=83=D0=BC?= =?UTF-8?q?=D0=BC=D0=B0=D1=80=D0=BD=D0=BE)=C2=BB=20+=20=C2=AB=D0=A1=D0=BD?= =?UTF-8?q?=D0=B8=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=83=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=BD=D1=8F=20=D0=B4=D0=BE=D1=85=D0=BE=D0=B4=D0=B0=C2=BB,=20?= =?UTF-8?q?=D0=B6=D0=B8=D1=80=D0=BD=D1=8B=D0=B9=20=D0=B7=D0=B0=D0=B3=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2=D0=BE=D0=BA=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD?= =?UTF-8?q?;=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=80=D1=8B=D1=85=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=20(=D1=81=D1=83=D0=BC=D0=BC=D0=B0=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=B3=D0=BE=D0=B4=D0=B0=D0=BC=20=E2=86=92=20income=5F5y),=20?= =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=B0=D1=81=D1=87=D1=91=D1=82/?= =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D0=B7=D0=B0=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5/DaData/=D1=88=D0=B0=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dokogen/models.py | 18 +++++++++++++++++- dokogen/ui.py | 35 +++++++++++++++++++++++------------ dokogen/variables.py | 15 ++++++++++----- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/dokogen/models.py b/dokogen/models.py index e58cca9..c52120e 100644 --- a/dokogen/models.py +++ b/dokogen/models.py @@ -130,7 +130,9 @@ class Company: profit_tax: str = "" # Сумма налога с прибыли за предыдущий год # Показатель 8 (ущерб субъекту КИИ): доходы за 5 лет и суммарный ущерб - income_year_1: str = "" # Доход за 5 лет назад (руб.) + # В форме — одна строка «Доход за 5 лет (суммарно)»; по годам разбивает сам оператор. + income_5y: str = "" # Доход за 5 лет суммарно (руб.) + income_year_1: str = "" # Доход за 5 лет назад (руб.) — обратная совместимость income_year_2: str = "" # Доход за 4 года назад (руб.) income_year_3: str = "" # Доход за 3 года назад (руб.) income_year_4: str = "" # Доход за 2 года назад (руб.) @@ -224,6 +226,20 @@ class Company: d["okved_extra"] = [] valid = {f.name for f in cls.__dataclass_fields__.values()} obj = cls(**{k: v for k, v in d.items() if k in valid}) + # Миграция: старые файлы с доходами по годам → суммарный доход за 5 лет + if not (getattr(obj, 'income_5y', '') or '').strip(): + _sum = 0.0 + _has = False + for i in range(1, 6): + v = str(getattr(obj, f'income_year_{i}', '') or '').strip() + if v: + try: + _sum += float(v.replace(' ', '').replace(',', '.').replace('\u00a0', '')) + _has = True + except Exception: + pass + if _has: + obj.income_5y = f"{_sum:,.0f}".replace(',', ' ') # Если в поле ФИО несколько человек — оставить одного (старые опросники) for fio_field in ('chief_fio', 'responsible_fio', 'admin_fio'): val = getattr(obj, fio_field, '') diff --git a/dokogen/ui.py b/dokogen/ui.py index e86dc7d..1b14c10 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -345,19 +345,12 @@ class DokoGenApp: entry.grid(row=i, column=1, padx=10, sticky=tk.EW, pady=1) self._company_entries[key] = entry - # Показатель 8 (ущерб субъекту КИИ): доходы за 5 лет + суммарный ущерб - ttk.Label(f_fin, text="Доходы за 5 лет (показатель 8):", - font=('', 9, 'bold')).grid(row=len(fin_fields), column=0, columnspan=2, - sticky=tk.W, pady=(8, 2)) + # Показатель 8 (ущерб субъекту КИИ): доход за 5 лет (суммарно) + суммарный ущерб ttk.Button(f_fin, text="🌐 Загрузить доходы из DaData (по ИНН)", command=self._load_incomes_from_dadata).grid( row=len(fin_fields), column=2, padx=5, sticky=tk.W) income_fields = [ - ("Доход за 5 лет назад (руб.):", "income_year_1"), - ("Доход за 4 года назад (руб.):", "income_year_2"), - ("Доход за 3 года назад (руб.):", "income_year_3"), - ("Доход за 2 года назад (руб.):", "income_year_4"), - ("Доход за прошлый год (руб.):", "income_year_5"), + ("Доход за 5 лет (суммарно, руб.):", "income_5y"), ("Снижение уровня дохода от нарушения критических процессов (руб.):", "income_loss"), ] for j, (label, key) in enumerate(income_fields): @@ -2360,11 +2353,21 @@ class DokoGenApp: return e e_profit = e_tax = None - # Доходы за 5 лет — из данных организации (поля «Доходы за 5 лет») + # Доходы за 5 лет — из данных организации (поле «Доход за 5 лет (суммарно)», + # иначе — обратная совместимость по годам) _incs_raw = [getattr(self.model, f'income_year_{i}', '') or '' for i in range(1, 6)] _incs = [_num(x) for x in _incs_raw] _incs_filled = [x for x in _incs if x is not None] - _d_avg = (sum(_incs_filled) / len(_incs_filled)) if _incs_filled else None + _inc5 = _num(getattr(self.model, 'income_5y', '') or '') + if _inc5: + _d_avg = _inc5 / 5.0 + _n_years = 5 + elif _incs_filled: + _d_avg = sum(_incs_filled) / len(_incs_filled) + _n_years = len(_incs_filled) + else: + _d_avg = None + _n_years = 0 _d_loss = _num(getattr(self.model, 'income_loss', '') or '') _bv8 = getattr(isys, 'balance_value', '') or '' _bvn8 = _num(_bv8) @@ -4247,9 +4250,16 @@ class DokoGenApp: # Последние 5 лет: income_year_5 (самый свежий) ... income_year_1 (самый старый) last5 = rows[-5:] mapping = [] + total = 0.0 for i, (y, inc) in enumerate(last5): # y — календарный год; income_year_1 — самый старый из пяти mapping.append((f'income_year_{i+1}', y, inc)) + total += inc + # Основное поле формы — «Доход за 5 лет (суммарно)» + entry = self._company_entries.get('income_5y') + if entry: + entry.delete(0, tk.END) + entry.insert(0, f"{total:,.0f}".replace(',', ' ')) for key, y, inc in mapping: entry = self._company_entries.get(key) if entry: @@ -4258,7 +4268,7 @@ class DokoGenApp: self._collect_company() years = ', '.join(f"{y}: {inc:,.0f} руб." for _, y, inc in mapping) self.log(f"🌐 Доходы из DaData загружены: {years}") - messagebox.showinfo("DaData", f"Доходы загружены из DaData:\n{years}") + messagebox.showinfo("DaData", f"Доходы загружены из DaData (сумма за 5 лет: {total:,.0f} руб.):\n{years}") def _select_main_okved(self): """Выбор основного ОКВЭД из словаря (всегда один).""" @@ -4623,6 +4633,7 @@ class DokoGenApp: # Показатель 8: доходы за 5 лет + суммарный ущерб от нарушения критических процессов income_map = [ + ('income_5y', ('income5Y', 'income_5y', 'income5y')), ('income_year_1', ('incomeYear1', 'income_year_1')), ('income_year_2', ('incomeYear2', 'income_year_2')), ('income_year_3', ('incomeYear3', 'income_year_3')), diff --git a/dokogen/variables.py b/dokogen/variables.py index e14c4d2..b8a5bf3 100644 --- a/dokogen/variables.py +++ b/dokogen/variables.py @@ -61,7 +61,8 @@ VARIABLE_DEFS = [ ('{{company_okved_extra}}', 'A', 'Дополнительные ОКВЭД', 'company:okved_extra'), ('{{company_profit}}', 'A', 'Сумма прибыли за предыдущий год (руб.)', 'company:profit'), ('{{company_profit_tax}}', 'A', 'Сумма налога с прибыли (руб.)', 'company:profit_tax'), - ('{{company_income_year_1}}', 'A', 'Доход за 5 лет назад (руб.) — показатель 8', 'company:income_year_1'), + ('{{company_income_5y}}', 'A', 'Доход за 5 лет суммарно (руб.) — показатель 8', 'company:income_5y'), + ('{{company_income_year_1}}', 'A', 'Доход за 5 лет назад (руб.) — показатель 8 (обратная совместимость)', 'company:income_year_1'), ('{{company_income_year_2}}', 'A', 'Доход за 4 года назад (руб.) — показатель 8', 'company:income_year_2'), ('{{company_income_year_3}}', 'A', 'Доход за 3 года назад (руб.) — показатель 8', 'company:income_year_3'), ('{{company_income_year_4}}', 'A', 'Доход за 2 года назад (руб.) — показатель 8', 'company:income_year_4'), @@ -428,10 +429,14 @@ def _calc_criterion_8_9(company, isys, num): d = _num(getattr(company, 'income_loss', '') or '') if d is None: d = _num(getattr(isys, 'balance_value', '') or '') - # Доходы за 5 лет - 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 + # Доходы за 5 лет: основное поле income_5y (суммарно), иначе — обратная совместимость по годам + 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 num == '8': # Фолбэк: ущерб не заполнен → оценка «доход за год × 5» (наихудший сценарий)