From c1ad4963197657902f255d3f82bdde8f1ef391fe Mon Sep 17 00:00:00 2001 From: prog1764 Date: Tue, 4 Aug 2026 23:20:04 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D0=BE=D1=85=D0=BE=D0=B4=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=BE=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=B8=D0=B7=20DaData=20(=D0=BF=D0=BE=D0=BA=D0=B0?= =?UTF-8?q?=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - verify.py: get_company_finance_dadata(inn, token) — findById/party, возвращает finance (последний отчётный год) + finance_history (по годам) - ui.py: кнопка «🌐 Загрузить доходы из DaData (по ИНН)» в блоке «Доходы за 5 лет (показатель 8)» — по ИНН организации подтягивает доходы за последние 5 лет и заполняет income_year_1..5 - если в тарифе DaData нет финансовой отчётности — понятное сообщение, доходы заполняются вручную --- dokogen/ui.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++ dokogen/verify.py | 32 +++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/dokogen/ui.py b/dokogen/ui.py index d4b0596..1f1dfb3 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -344,6 +344,9 @@ class DokoGenApp: 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)) + 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"), @@ -3276,6 +3279,76 @@ class DokoGenApp: return True return False + def _load_incomes_from_dadata(self): + """🌐 Загрузить доходы организации за 5 лет из DaData (finance_history / finance).""" + from .api_settings import load_api_settings + settings = load_api_settings() + if not settings.get('dadata_token'): + messagebox.showwarning("DaData", "Не задан API-ключ DaData.\nМеню «Файл → Настройки»") + return + inn = self._company_entries.get('inn', None) + inn_val = (inn.get().strip() if inn else '') or getattr(self.model, 'inn', '') or '' + if not inn_val: + messagebox.showinfo("DaData", "Сначала укажите ИНН организации (вкладка «Организация»).") + return + self.log(f"🌐 Запрос доходов из DaData (ИНН {inn_val})…") + + def _run(): + try: + from .verify import get_company_finance_dadata + res, err = get_company_finance_dadata(inn_val, settings['dadata_token']) + self.root.after(0, lambda: self._load_incomes_done(res, err)) + except Exception as e: + self.root.after(0, lambda: self._load_incomes_done(None, str(e))) + + threading.Thread(target=_run, daemon=True).start() + + def _load_incomes_done(self, res, err): + if err: + messagebox.showerror("DaData", f"Не удалось получить доходы:\n{err}") + self.log(f"❌ Доходы DaData: {err}") + return + finance = res.get('finance') or {} + history = res.get('history') or [] + # Доход за последний год из finance (income/revenue) + last_income = finance.get('income') or finance.get('revenue') or None + last_year = finance.get('year') or None + + # history: список {year, income, revenue, ...} — сортируем по году + rows = [] + for h in history: + y = h.get('year') + inc = h.get('income') or h.get('revenue') or None + if y and inc: + rows.append((int(y), float(inc))) + if last_year and last_income: + rows.append((int(last_year), float(last_income))) + rows = sorted(set(rows), key=lambda x: x[0]) + + if not rows: + messagebox.showinfo( + "DaData", + f"Финансовые показатели для ИНН {res.get('inn')} в ответе DaData отсутствуют " + f"(нужен тариф DaData с финансовой отчётностью).\n" + f"Заполните доходы за 5 лет вручную.") + return + + # Последние 5 лет: income_year_5 (самый свежий) ... income_year_1 (самый старый) + last5 = rows[-5:] + mapping = [] + for i, (y, inc) in enumerate(last5): + # y — календарный год; income_year_1 — самый старый из пяти + mapping.append((f'income_year_{i+1}', y, inc)) + for key, y, inc in mapping: + entry = self._company_entries.get(key) + if entry: + entry.delete(0, tk.END) + entry.insert(0, f"{inc:,.0f}".replace(',', ' ')) + 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}") + def _select_main_okved(self): """Выбор основного ОКВЭД из словаря (всегда один).""" items = self._load_okved_dict() diff --git a/dokogen/verify.py b/dokogen/verify.py index 0558c44..77338c4 100644 --- a/dokogen/verify.py +++ b/dokogen/verify.py @@ -251,6 +251,38 @@ def check_declension_morpher(text): # ============================================================ # DADATA — проверка организации по ИНН (нужен API-ключ) # ============================================================ +def get_company_finance_dadata(inn, token): + """Доходы организации по годам через DaData (findById/party → finance/finance_history). + + Возвращает (dict, ошибка): dict вида + {'finance': {...}, 'history': [{'year': 2023, 'income': 123456, ...}, ...]} + finance — показатели за последний отчётный год; + history — история по годам (если доступна в тарифе DaData). + """ + if not token: + return None, "Не задан API-ключ DaData (Файл → Настройки → API)" + inn = (inn or '').replace(' ', '') + if not inn or not inn.isdigit(): + return None, "Введите ИНН для загрузки доходов" + headers = { + 'Content-Type': 'application/json', + 'Authorization': f'Token {token}', + } + js, err = _http_json(DADATA_URL, data={'query': inn}, headers=headers) + if err: + return None, err + suggestions = (js or {}).get('suggestions') or [] + if not suggestions: + return None, f"DaData: организация с ИНН {inn} не найдена" + d = suggestions[0].get('data') or {} + return { + 'finance': d.get('finance') or {}, + 'history': d.get('finance_history') or [], + 'inn': d.get('inn', inn), + 'name': (d.get('name') or {}).get('short_with_opf') or suggestions[0].get('value', ''), + }, None + + def check_company_dadata(inn, token): """Проверка организации по ИНН/ОГРН через DaData.