📊
Банковские выписки и формат 1С-банк
Парсинг банковских выписок в форматах 1С-банк (1CClientBankExchange), CSV/XLSX банков, извлечение операций, сверка оборотов
Системный промпт
Банковские выписки и формат 1С-банк
Паттерны Python-кода для парсинга банковских выписок. Файлы доступны в /tmp/.
Формат 1CClientBankExchange
Стандартный текстовый формат обмена между 1С:Предприятие и системами «Клиент-Банк». Кодировка: Windows-1251.
Структура файла
1CClientBankExchange
ВерсияФормата=1.03
Кодировка=Windows
Отправитель=Бухгалтерия предприятия
Получатель=Банк
ДатаСоздания=20.02.2026
ВремяСоздания=10:30:00
ДатаНачала=01.02.2026
ДатаКонца=20.02.2026
РасsчСчёт=40702810123456789012
СекцияРас662Счёт=
НачsальныйОстаток=1000000.00
ВсегоПоступило=500000.00
ВсегоСписано=300000.00
КонечныйОстаток=1200000.00
СекцияРасчСчёт=Конец
СекцияДокумент=Платёжное поручение
Номер=123
Дата=15.02.2026
Сумма=50000.00
ПлательщикСчёт=40702810123456789012
ПлательщикИНН=7701234567
ПлательщикКПП=770101001
Плательщик1=ООО "Наша компания"
ПолучательСчёт=40702810987654321098
ПолучательИНН=7709876543
ПолучательКПП=770901001
Получатель1=АО "Контрагент"
ПолучательБИК=044525225
ПолучательБанк1=ПАО СБЕРБАНК
НазначениеПлатежа=Оплата по счёту №45 от 10.02.2026, в т.ч. НДС (22%) 9166.67
КонецДокумента
Ключевые поля секции документа
| Поле | Описание | Пример |
|---|---|---|
Номер | Номер платёжного поручения | 123 |
Дата | Дата документа | 15.02.2026 |
Сумма | Сумма платежа | 50000.00 |
ПлательщикСчёт | Расчётный счёт плательщика | 40702810123456789012 |
ПлательщикИНН | ИНН плательщика | 7701234567 |
ПлательщикКПП | КПП плательщика | 770101001 |
Плательщик1 | Наименование плательщика | ООО "Наша компания" |
ПолучательСчёт | Расчётный счёт получателя | 40702810987654321098 |
ПолучательИНН | ИНН получателя | 7709876543 |
ПолучательБИК | БИК банка получателя | 044525225 |
НазначениеПлатежа | Назначение платежа | Текст |
ВидОплаты | Вид операции (01, 02, 06...) | 01 |
БЛОК 1: Парсинг формата 1CClientBankExchange
import subprocess
subprocess.run(['pip', 'install', 'pandas'], capture_output=True)
import re
import json
import pandas as pd
# Чтение файла в кодировке windows-1251
with open("/tmp/bank_statement.txt", "r", encoding="windows-1251") as f:
content = f.read()
# Извлечение заголовка
header = {}
header_match = re.search(r"ДатаНачала=(.+)", content)
if header_match:
header["дата_начала"] = header_match.group(1).strip()
header_match = re.search(r"ДатаКонца=(.+)", content)
if header_match:
header["дата_конца"] = header_match.group(1).strip()
for field in ["НачальныйОстаток", "ВсегоПоступило", "ВсегоСписано", "КонечныйОстаток"]:
m = re.search(rf"{field}=(.+)", content)
if m:
header[field] = float(m.group(1).strip().replace(",", "."))
account_match = re.search(r"РасsчСчёт=(\d+)", content)
if not account_match:
account_match = re.search(r"РасчСчет=(\d+)", content)
if account_match:
header["расч_счёт"] = account_match.group(1)
# Извлечение документов (платёжных поручений)
doc_blocks = re.findall(r"СекцияДокумент=(.+?)\nКонецДокумента", content, re.DOTALL)
documents = []
for block in doc_blocks:
doc = {}
for line in block.strip().split("\n"):
if "=" in line:
key, _, value = line.partition("=")
doc[key.strip()] = value.strip()
documents.append(doc)
# Преобразуем в таблицу
rows = []
for doc in documents:
rows.append({
"Номер": doc.get("Номер", ""),
"Дата": doc.get("Дата", ""),
"Сумма": float(doc.get("Сумма", "0").replace(",", ".")),
"Плательщик": doc.get("Плательщик1", ""),
"ПлательщикИНН": doc.get("ПлательщикИНН", ""),
"ПлательщикСчёт": doc.get("ПлательщикСчёт", ""),
"Получатель": doc.get("Получатель1", ""),
"ПолучательИНН": doc.get("ПолучательИНН", ""),
"ПолучательСчёт": doc.get("ПолучательСчёт", ""),
"ПолучательБИК": doc.get("ПолучательБИК", ""),
"Назначение": doc.get("НазначениеПлатежа", ""),
})
df = pd.DataFrame(rows)
df.to_excel(cw.output_path("bank_statement.xlsx"), index=False)
print(f"Заголовок: {json.dumps(header, ensure_ascii=False, indent=2)}")
print(f"Документов: {len(documents)}")
print(f"Общая сумма: {df['Сумма'].sum():,.2f}")
БЛОК 2: Парсинг CSV/XLSX выписок банков
Сбербанк Бизнес Онлайн (CSV)
import subprocess
subprocess.run(['pip', 'install', 'pandas'], capture_output=True)
import pandas as pd
# Сбербанк обычно: разделитель ";", кодировка windows-1251
df = pd.read_csv(
"/tmp/sber_statement.csv",
sep=";",
encoding="windows-1251",
decimal=",",
)
# Стандартные колонки Сбера
column_map = {
"Номер документа": "номер",
"Дата операции": "дата",
"Дебет": "дебет",
"Кредит": "кредит",
"Наименование контрагента": "контрагент",
"ИНН контрагента": "инн",
"Назначение платежа": "назначение",
"Номер счёта контрагента": "счёт",
}
# Переименовываем найденные колонки
rename = {}
for orig, target in column_map.items():
for col in df.columns:
if orig.lower() in col.lower():
rename[col] = target
break
df = df.rename(columns=rename)
# Приводим суммы к числам
for col in ["дебет", "кредит"]:
if col in df.columns:
df[col] = pd.to_numeric(df[col].astype(str).str.replace(r"\s", "", regex=True).str.replace(",", "."), errors="coerce").fillna(0)
df.to_excel(cw.output_path("sber_parsed.xlsx"), index=False)
print(f"Строк: {len(df)}")
print(f"Дебет итого: {df.get('дебет', pd.Series([0])).sum():,.2f}")
print(f"Кредит итого: {df.get('кредит', pd.Series([0])).sum():,.2f}")
Тинькофф Бизнес (XLSX)
import subprocess
subprocess.run(['pip', 'install', 'pandas', 'openpyxl'], capture_output=True)
import pandas as pd
df = pd.read_excel("/tmp/tinkoff_statement.xlsx")
# Стандартные колонки Тинькофф
column_map = {
"Номер": "номер",
"Дата операции": "дата",
"Сумма": "сумма",
"Тип": "тип", # Пополнение / Списание
"Контрагент": "контрагент",
"ИНН контрагента": "инн",
"Назначение платежа": "назначение",
"Счёт контрагента": "счёт",
}
rename = {}
for orig, target in column_map.items():
for col in df.columns:
if orig.lower() in col.lower():
rename[col] = target
break
df = df.rename(columns=rename)
# Разделяем на дебет/кредит
if "тип" in df.columns and "сумма" in df.columns:
df["дебет"] = df.apply(lambda r: abs(r["сумма"]) if "списан" in str(r["тип"]).lower() else 0, axis=1)
df["кредит"] = df.apply(lambda r: abs(r["сумма"]) if "поступл" in str(r["тип"]).lower() or "пополн" in str(r["тип"]).lower() else 0, axis=1)
df.to_excel(cw.output_path("tinkoff_parsed.xlsx"), index=False)
print(f"Строк: {len(df)}")
БЛОК 3: Анализ выписки
Сверка оборотов
import subprocess
subprocess.run(['pip', 'install', 'pandas'], capture_output=True)
import pandas as pd
import json
# df — уже загруженная выписка с колонками: дата, дебет, кредит, контрагент, инн, назначение
# header — словарь с НачальныйОстаток, ВсегоПоступило, ВсегоСписано, КонечныйОстаток
reconciliation = {
"начальный_остаток": header.get("НачальныйОстаток", 0),
"обороты_дебет_факт": df["дебет"].sum(),
"обороты_кредит_факт": df["кредит"].sum(),
"обороты_дебет_заявленные": header.get("ВсегоСписано", 0),
"обороты_кредит_заявленные": header.get("ВсегоПоступило", 0),
}
reconciliation["расхождение_дебет"] = abs(
reconciliation["обороты_дебет_факт"] - reconciliation["обороты_дебет_заявленные"]
)
reconciliation["расхождение_кредит"] = abs(
reconciliation["обороты_кредит_факт"] - reconciliation["обороты_кредит_заявленные"]
)
expected_end = (
reconciliation["начальный_остаток"]
+ reconciliation["обороты_кредит_факт"]
- reconciliation["обороты_дебет_факт"]
)
reconciliation["конечный_остаток_расчётный"] = expected_end
reconciliation["конечный_остаток_заявленный"] = header.get("КонечныйОстаток", 0)
reconciliation["сверка_пройдена"] = abs(expected_end - header.get("КонечныйОстаток", 0)) < 0.01
with open(cw.output_path("reconciliation.json"), "w", encoding="utf-8") as f:
json.dump(reconciliation, f, ensure_ascii=False, indent=2)
print("Сверка оборотов:")
for k, v in reconciliation.items():
print(f" {k}: {v}")
Группировка по контрагентам
import subprocess
subprocess.run(['pip', 'install', 'pandas'], capture_output=True)
import pandas as pd
# Группируем по ИНН контрагента
by_inn = df.groupby("инн").agg(
контрагент=("контрагент", "first"),
операций=("номер", "count"),
дебет_итого=("дебет", "sum"),
кредит_итого=("кредит", "sum"),
).reset_index()
by_inn["сальдо"] = by_inn["кредит_итого"] - by_inn["дебет_итого"]
by_inn = by_inn.sort_values("дебет_итого", ascending=False)
by_inn.to_excel(cw.output_path("by_counterparty.xlsx"), index=False)
print(f"Уникальных контрагентов: {len(by_inn)}")
print("\nТоп-5 по списаниям:")
print(by_inn.head().to_string(index=False))
Извлечение НДС из назначения платежа
import re
import pandas as pd
def extract_vat(text: str) -> float | None:
"""Извлекает сумму НДС из назначения платежа."""
if not text:
return None
# Паттерны НДС в назначении
patterns = [
r"НДС\s*(?:\(\d+%?\))?\s*[-—]\s*([\d\s]+[,.]?\d*)",
r"в\s*т\.?\s*ч\.?\s*НДС\s*(?:\(\d+%?\))?\s*([\d\s]+[,.]?\d*)",
r"НДС\s*([\d\s]+[,.]?\d*)\s*руб",
r"сумма\s*НДС\s*([\d\s]+[,.]?\d*)",
]
for pattern in patterns:
match = re.search(pattern, text, re.IGNORECASE)
if match:
amount_str = match.group(1).replace(" ", "").replace(",", ".")
try:
return float(amount_str)
except ValueError:
continue
# Проверяем "Без НДС" / "НДС не облагается"
if re.search(r"без\s*НДС|НДС\s*не\s*облаг", text, re.IGNORECASE):
return 0.0
return None
df["ндс"] = df["назначение"].apply(extract_vat)
vat_total = df["ндс"].dropna().sum()
print(f"Общая сумма НДС в выписке: {vat_total:,.2f}")
БЛОК 4: Конвертация форматов
1CClientBankExchange → Excel
Используй БЛОК 1: парсинг формата 1CClientBankExchange.
Excel → 1CClientBankExchange
import pandas as pd
from datetime import datetime
def to_1c_bank_format(df: pd.DataFrame, account: str, output_path: str) -> str:
"""Конвертирует DataFrame в формат 1CClientBankExchange."""
now = datetime.now()
lines = [
"1CClientBankExchange",
"ВерсияФормата=1.03",
"Кодировка=Windows",
"Отправитель=",
"Получатель=",
f"ДатаСоздания={now.strftime('%d.%m.%Y')}",
f"ВремяСоздания={now.strftime('%H:%M:%S')}",
f"ДатаНачала={df['дата'].min()}",
f"ДатаКонца={df['дата'].max()}",
f"РасsчСчёт={account}",
]
for _, row in df.iterrows():
is_outgoing = float(row.get("дебет", 0)) > 0
lines.append("СекцияДокумент=Платёжное поручение")
lines.append(f"Номер={row.get('номер', '')}")
lines.append(f"Дата={row.get('дата', '')}")
lines.append(f"Сумма={row.get('дебет', 0) if is_outgoing else row.get('кредит', 0):.2f}")
if is_outgoing:
lines.append(f"ПлательщикСчёт={account}")
lines.append(f"ПолучательСчёт={row.get('счёт', '')}")
lines.append(f"ПолучательИНН={row.get('инн', '')}")
lines.append(f"Получатель1={row.get('контрагент', '')}")
else:
lines.append(f"ПлательщикСчёт={row.get('счёт', '')}")
lines.append(f"ПлательщикИНН={row.get('инн', '')}")
lines.append(f"Плательщик1={row.get('контрагент', '')}")
lines.append(f"ПолучательСчёт={account}")
lines.append(f"НазначениеПлатежа={row.get('назначение', '')}")
lines.append("КонецДокумента")
content = "\r\n".join(lines)
with open(output_path, "w", encoding="windows-1251") as f:
f.write(content)
return output_path
БЛОК 5: Валидация банковских реквизитов
def validate_bik(bik: str) -> tuple[bool, str]:
"""Проверка БИК (9 цифр)."""
bik = bik.strip()
if not bik.isdigit() or len(bik) != 9:
return False, "БИК должен содержать 9 цифр"
if not bik.startswith("04"):
return False, "БИК должен начинаться с 04"
return True, "OK"
def validate_account(account: str, bik: str) -> tuple[bool, str]:
"""Проверка расчётного счёта (20 цифр) по БИК."""
account = account.strip()
bik = bik.strip()
if not account.isdigit() or len(account) != 20:
return False, "Счёт должен содержать 20 цифр"
if not bik.isdigit() or len(bik) != 9:
return False, "Для проверки нужен корректный БИК"
# Контрольный ключ
control_str = bik[-3:] + account
weights = [7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1]
total = sum(int(control_str[i]) * weights[i] for i in range(23))
if total % 10 != 0:
return False, "Неверный контрольный ключ счёта"
return True, "OK"
БЛОК 6: Типичные команды пользователя
| Команда | Действие |
|---|---|
| "Разобрать выписку из 1С-банка" | БЛОК 1: Парсинг 1CClientBankExchange |
| "Загрузить выписку Сбербанка" | БЛОК 2: Парсинг CSV Сбербанка |
| "Загрузить выписку Тинькофф" | БЛОК 2: Парсинг XLSX Тинькофф |
| "Сверить обороты" | БЛОК 3: Сверка оборотов |
| "Группировка по контрагентам" | БЛОК 3: Группировка по ИНН |
| "Извлечь НДС из платежей" | БЛОК 3: Извлечение НДС |
| "Конвертировать в формат 1С" | БЛОК 4: Excel → 1CClientBankExchange |
| "Проверить реквизиты" | БЛОК 5: Валидация БИК и счёта |
БЛОК 7: Автокатегоризация платежей по статьям ДДС
Паттерн: Категоризация по назначению платежа
import subprocess
subprocess.run(['pip', 'install', 'pandas'], capture_output=True)
import re
import pandas as pd
# Словарь паттернов для категоризации
DDS_CATEGORIES = {
"Аренда": [
r"аренд[аы]", r"субаренд", r"помещени[еяй]", r"офис",
r"склад", r"нежил", r"коммерч.*площ",
],
"Зарплата": [
r"заработн", r"зарплат", r"аванс.*сотрудник", r"оплата труда",
r"з/?п", r"перечисление\s+зп", r"реестр\s+на\s+зачисление",
],
"Налоги и сборы": [
r"налог", r"ндс", r"ндфл", r"усн", r"есхн",
r"ифнс", r"фнс", r"енвд", r"страхов.*взнос",
r"пфр", r"фсс", r"сфр", r"пени.*налог", r"штраф.*налог",
r"госпошлин", r"торговый\s+сбор",
],
"Закупка товаров": [
r"за\s+товар", r"поставк[аеу]", r"по\s+счёту\s+№", r"по\s+счету\s+№",
r"закупк[аеу]", r"по\s+накладной", r"комплектующ", r"материал",
r"сырь[еёя]", r"оборудовани[еяй]",
],
"Услуги": [
r"за\s+услуг[иу]", r"консультац", r"юридич", r"бухгалтер",
r"аудит", r"обслуживан", r"сервис", r"абонент.*плат",
r"подписк[аеу]", r"лицензи[яю]", r"по\s+договору.*услуг",
],
"Реклама и маркетинг": [
r"реклам[аеу]", r"маркетинг", r"продвижен", r"seo",
r"контекстн", r"таргет", r"яндекс.*директ", r"vk\s*ads",
r"smm", r"полиграф", r"баннер",
],
"Связь и интернет": [
r"связ[иь]", r"телеком", r"интернет", r"телефон",
r"мобильн.*связ", r"хостинг", r"домен", r"сервер",
r"облачн", r"cloud",
],
"Транспорт и логистика": [
r"транспорт", r"доставк[аеу]", r"логистик", r"перевозк",
r"грузопер", r"курьер", r"экспедиц", r"такси",
r"гсм", r"бензин", r"топлив",
],
"Коммунальные платежи": [
r"коммунал", r"электроэнерг", r"водоснабж", r"теплоснабж",
r"газоснабж", r"жкх", r"мусор", r"тбо", r"тко",
],
"Банковские комиссии": [
r"комисси[яю].*банк", r"банковск.*комисси", r"обслуживание\s+счёт",
r"рко", r"расчётно.*кассов", r"эквайринг", r"инкасс",
],
"Займы и кредиты": [
r"кредит", r"займ", r"заём", r"процент.*по\s+договор",
r"погашени[еяй].*основн", r"тело\s+кредит", r"лизинг",
r"овердрафт",
],
"Возвраты": [
r"возврат", r"рекламаци", r"компенсаци[яю].*покуп",
],
"Дивиденды и прибыль": [
r"дивиденд", r"распределени[еяй].*прибыл",
r"выплат[аеу].*учредител", r"доход.*участник",
],
}
def categorize_payment(purpose: str) -> str:
"""Определяет статью ДДС по назначению платежа."""
if not purpose:
return "Прочее"
purpose_lower = purpose.lower()
for category, patterns in DDS_CATEGORIES.items():
for pattern in patterns:
if re.search(pattern, purpose_lower):
return category
return "Прочее"
def categorize_dataframe(df: pd.DataFrame, purpose_col: str = "назначение") -> pd.DataFrame:
"""Добавляет колонку 'статья_ддс' к DataFrame с банковской выпиской."""
df = df.copy()
df["статья_ддс"] = df[purpose_col].apply(categorize_payment)
return df
Паттерн: Сводка по статьям ДДС
import pandas as pd
def dds_summary(df: pd.DataFrame) -> pd.DataFrame:
"""
Сводка расходов/поступлений по статьям ДДС.
Предполагает наличие колонок: статья_ддс, дебет, кредит.
"""
summary = df.groupby("статья_ддс").agg(
операций=("статья_ддс", "count"),
списания=("дебет", "sum"),
поступления=("кредит", "sum"),
).reset_index()
summary["сальдо"] = summary["поступления"] - summary["списания"]
summary = summary.sort_values("списания", ascending=False)
# Итого
totals = pd.DataFrame([{
"статья_ддс": "ИТОГО",
"операций": summary["операций"].sum(),
"списания": summary["списания"].sum(),
"поступления": summary["поступления"].sum(),
"сальдо": summary["сальдо"].sum(),
}])
summary = pd.concat([summary, totals], ignore_index=True)
return summary
# Использование:
# df = categorize_dataframe(bank_df)
# report = dds_summary(df)
# report.to_excel(cw.output_path("dds_report.xlsx"), index=False)
# print(report.to_string(index=False))
Паттерн: Полный пайплайн — выписка с категоризацией в Excel
import subprocess
subprocess.run(['pip', 'install', 'pandas', 'openpyxl'], capture_output=True)
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
from openpyxl.utils import get_column_letter
# Цвета для статей ДДС
DDS_COLORS = {
"Аренда": "FFF2CC",
"Зарплата": "D9E2F3",
"Налоги и сборы": "FCE4D6",
"Закупка товаров": "E2EFDA",
"Услуги": "D6DCE4",
"Реклама и маркетинг": "F8CBAD",
"Связь и интернет": "BDD7EE",
"Транспорт и логистика": "DBDBDB",
"Коммунальные платежи": "FFE699",
"Банковские комиссии": "F4B084",
"Займы и кредиты": "C9C9C9",
"Возвраты": "A9D18E",
"Дивиденды и прибыль": "B4C6E7",
"Прочее": "FFFFFF",
}
def export_categorized_statement(df: pd.DataFrame, output_path: str) -> str:
"""Экспорт выписки с категоризацией в Excel с цветовой разметкой."""
wb = Workbook()
# Лист 1: Детализация
ws1 = wb.active
ws1.title = "Выписка"
BORDER = Border(*(Side(style="thin") for _ in range(4)))
BOLD = Font(bold=True)
headers = ["Дата", "Номер", "Контрагент", "ИНН", "Дебет", "Кредит", "Назначение", "Статья ДДС"]
widths = [12, 10, 25, 14, 14, 14, 40, 20]
cols = ["дата", "номер", "контрагент", "инн", "дебет", "кредит", "назначение", "статья_ддс"]
for c, (h, w) in enumerate(zip(headers, widths), 1):
cell = ws1.cell(1, c, h)
cell.font = BOLD
cell.border = BORDER
ws1.column_dimensions[get_column_letter(c)].width = w
for r, (_, row) in enumerate(df.iterrows(), 2):
for c, col in enumerate(cols, 1):
cell = ws1.cell(r, c, row.get(col, ""))
cell.border = BORDER
if c in (5, 6):
cell.number_format = '# ##0.00'
# Цвет по категории
category = str(row.get("статья_ддс", "Прочее"))
color = DDS_COLORS.get(category, "FFFFFF")
fill = PatternFill("solid", start_color=color)
ws1.cell(r, 8).fill = fill
# Лист 2: Сводка по ДДС
ws2 = wb.create_sheet("Сводка ДДС")
summary = df.groupby("статья_ддс").agg(
операций=("статья_ддс", "count"),
списания=("дебет", "sum"),
поступления=("кредит", "sum"),
).reset_index()
summary["сальдо"] = summary["поступления"] - summary["списания"]
summary = summary.sort_values("списания", ascending=False)
sum_headers = ["Статья ДДС", "Операций", "Списания", "Поступления", "Сальдо"]
for c, h in enumerate(sum_headers, 1):
cell = ws2.cell(1, c, h)
cell.font = BOLD
cell.border = BORDER
ws2.column_dimensions["A"].width = 25
for r, (_, row) in enumerate(summary.iterrows(), 2):
ws2.cell(r, 1, row["статья_ддс"]).border = BORDER
color = DDS_COLORS.get(str(row["статья_ддс"]), "FFFFFF")
ws2.cell(r, 1).fill = PatternFill("solid", start_color=color)
ws2.cell(r, 2, int(row["операций"])).border = BORDER
for c, col in [(3, "списания"), (4, "поступления"), (5, "сальдо")]:
cell = ws2.cell(r, c, float(row[col]))
cell.border = BORDER
cell.number_format = '# ##0.00'
wb.save(output_path)
return output_path
# Использование:
# df = categorize_dataframe(bank_df)
# export_categorized_statement(df, cw.output_path("categorized_statement.xlsx"))
Skill-файл содержит паттерны кода для парсинга банковских выписок. Выполняй код через sandbox_bash.
Категория
📊 Документы и расчёты
Платформа
Сам Решу
Попробуйте этот навык
Зарегистрируйтесь и используйте навык «Банковские выписки и формат 1С-банк» бесплатно.