Технічне завдання: Гнучкий модуль імпорту банківських виписок у K2 ERP
1. Загальна інформація
1.1. Назва задачі
Створення гнучкого налаштовуваного модуля імпорту банківських виписок у K2 ERP.
1.2. Мета розробки
Метою розробки є створення універсального механізму імпорту банківських виписок, який дозволить K2 ERP приймати виписки з будь-якого українського банку без необхідності доробки програмного коду для кожного нового формату.
Модуль має забезпечити:
- імпорт банківських виписок з файлів різних форматів;
- автоматичне розпізнавання відомих форматів;
- ручне налаштування мапінгу для невідомих форматів;
- збереження шаблонів імпорту для повторного використання;
- приведення всіх зовнішніх форматів до єдиної внутрішньої структури K2 ERP;
- попередній перегляд результатів імпорту;
- валідацію та дедублікацію операцій;
- журналювання та аудит імпорту;
- можливість додавання нового формату банку без зміни програмного коду.
1.3. Основна бізнес-проблема
В українських банках відсутній єдиний практичний формат клієнтської банківської виписки у CSV, DBF або Excel, який однаково використовується всіма банками.
Різні банки можуть використовувати:
- різні типи файлів;
- різні назви колонок;
- різне кодування;
- різні правила визначення дебету та кредиту;
- різний формат дат;
- різну структуру контрагентів;
- різні API;
- різні версії одного й того самого формату в межах одного банку.
Тому модуль має бути не набором жорстко закодованих імпортів, а гнучкою платформою налаштування імпорту банківських виписок.
1.4. Основна ідея рішення
Усі зовнішні формати виписок мають перетворюватися у єдиний внутрішній формат K2 ERP.
Будь-яка банківська виписка
↓
Визначення формату
↓
Парсер або шаблон мапінгу
↓
Єдина внутрішня структура K2 ERP
↓
Перевірка
↓
Попередній перегляд
↓
Створення банківських операцій у K2 ERP
2. Область застосування
Модуль використовується в K2 ERP для завантаження банківських виписок у бухгалтерський, фінансовий або управлінський облік.
Модуль має підтримувати імпорт з таких джерел:
- файл, завантажений користувачем;
- файл, отриманий із клієнт-банку;
- файл, сформований банківським API;
- відповідь API банку;
- стандартизований XML ISO 20022 camt.053;
- ручно налаштований шаблон імпорту.
3. Терміни та скорочення
| Термін | Опис |
|---|---|
| Банківська виписка | Файл або відповідь API, що містить рух коштів за банківським рахунком за певний період. |
| Парсер | Python-функція або набір функцій, які читають конкретний формат виписки і перетворюють його у внутрішній формат K2 ERP. |
| Шаблон імпорту | Налаштування, яке описує структуру файлу, відповідність колонок, правила сум, дат, дебету та кредиту. |
| Мапінг | Зіставлення колонок або полів зовнішньої виписки з полями K2 ERP. |
| Preview | Попередній перегляд результату імпорту до створення реальних банківських операцій. |
| Commit | Остаточне підтвердження імпорту і створення документів або операцій у K2 ERP. |
| UETR | Унікальний ідентифікатор платежу, якщо він присутній у банківській виписці. |
| ISO 20022 camt.053 | XML-формат банківської виписки за рахунком. |
4. Підтримувані типи файлів
Модуль має підтримувати такі формати:
| Формат | Опис | Обов’язковість |
|---|---|---|
| CSV | Текстовий файл з роздільниками | Обов’язково |
| DBF | Табличний формат, поширений у клієнт-банках | Обов’язково |
| XLS | Excel 97–2003 | Бажано |
| XLSX | Сучасний Excel-формат | Обов’язково |
| XML | XML-виписки банків, зокрема ISO 20022 camt.053 | Обов’язково |
| JSON | Відповіді API банків | Бажано |
| TXT | Текстові виписки довільної структури | Бажано |
5. Загальний принцип роботи модуля
Модуль має працювати за таким сценарієм:
- Користувач відкриває форму імпорту банківської виписки у K2 ERP.
- Користувач вибирає організацію та банківський рахунок.
- Користувач завантажує файл або вибирає API-джерело.
- Система визначає тип файлу.
- Система визначає кодування, роздільник, структуру колонок або XML/JSON-схему.
- Система шукає відповідний шаблон імпорту.
- Якщо шаблон знайдено — система застосовує його автоматично.
- Якщо шаблон не знайдено — система відкриває майстер ручного мапінгу.
- Система читає рядки виписки.
- Система приводить рядки до внутрішньої структури K2 ERP.
- Система виконує валідацію.
- Система виконує дедублікацію.
- Система показує користувачу попередній перегляд.
- Користувач підтверджує імпорт.
- Система створює банківські операції або документи K2 ERP.
- Система зберігає журнал імпорту.
6. Архітектура модуля відповідно до підходу K2 ERP Python
Модуль імпорту банківських виписок має бути реалізований як налаштовуваний Python-модуль K2 ERP, який викликається з інтерфейсу K2 ERP та працює з внутрішніми таблицями, довідниками, документами й журналами системи.
Модуль не повинен вимагати створення окремого вебзастосунку, окремого Flask/FastAPI-сервера або зовнішнього UI. Візуальна частина має використовувати стандартні механізми K2 ERP: форми, таблиці, дії, меню, редактор скриптів, стандартні grid-компоненти та права доступу.
6.1. Принцип реалізації
Основна логіка імпорту має бути винесена в Python-скрипти K2 ERP.
Користувач у K2 ERP
↓
Форма імпорту банківської виписки
↓
Python-скрипт імпорту
↓
Визначення формату файлу
↓
Застосування шаблону або ручного мапінгу
↓
Запис у тимчасові таблиці імпорту
↓
Попередній перегляд
↓
Підтвердження користувачем
↓
Створення банківських операцій у K2 ERP
6.2. Рекомендована структура Python-скриптів
| Скрипт | Призначення |
|---|---|
| bank_import_main.py | Головна точка входу модуля імпорту. |
| bank_import_detect.py | Визначення типу файлу, кодування, роздільника та шаблону. |
| bank_import_csv.py | Обробка CSV-файлів. |
| bank_import_dbf.py | Обробка DBF-файлів. |
| bank_import_xlsx.py | Обробка XLS/XLSX-файлів. |
| bank_import_camt053.py | Обробка XML ISO 20022 camt.053. |
| bank_import_mapping.py | Застосування правил мапінгу. |
| bank_import_validate.py | Валідація рядків виписки. |
| bank_import_deduplicate.py | Пошук дублікатів. |
| bank_import_commit.py | Остаточне створення операцій у K2 ERP. |
| bank_import_api_privat.py | Імпорт через API ПриватБанку. |
| bank_import_api_mono.py | Імпорт через API monobank. |
| bank_import_utils.py | Допоміжні функції: дати, суми, IBAN, JSON, хеші. |
6.3. Рекомендована структура модуля
k2bankimport/
__init__.py
bank_import_main.py
bank_import_detect.py
bank_import_csv.py
bank_import_dbf.py
bank_import_xlsx.py
bank_import_camt053.py
bank_import_mapping.py
bank_import_validate.py
bank_import_deduplicate.py
bank_import_commit.py
bank_import_api_privat.py
bank_import_api_mono.py
bank_import_utils.py
Усі скрипти мають бути орієнтовані на виклик із K2 ERP, а не на самостійний запуск як окремий вебсервіс.
6.4. Важлива архітектурна вимога
У ТЗ не потрібно жорстко закладати зовнішні controllers/services, якщо це не є стандартом K2 ERP.
Замість архітектури виду:
FileUploadController
BankApiController
ParserRegistry
ValidationService
ImportCommitService
для K2 ERP Python потрібно використовувати логіку:
Форма K2 ERP
Дія меню K2 ERP
Python-скрипт імпорту
Таблиці налаштувань K2 ERP
Таблиці попереднього імпорту
Шаблони мапінгу
Функції парсингу
Функції валідації
Функції створення документів K2 ERP
Журнал аудиту
7. Внутрішня універсальна модель даних
7.1. Об’єкт BankStatementImport
Це заголовок імпорту банківської виписки.
| Поле | Тип | Опис |
|---|---|---|
| id | UUID / bigint | Ідентифікатор імпорту. |
| source_type | enum | FILE / API / MANUAL. |
| org_id | FK | Організація в K2 ERP. |
| bank_id | FK | Банк у довіднику K2 ERP. |
| bank_code | string | Код банку. |
| bank_name | string | Назва банку. |
| account_id | FK | Банківський рахунок K2 ERP. |
| account_iban | string | IBAN рахунку. |
| currency | string | Валюта. |
| period_from | date | Початок періоду. |
| period_to | date | Кінець періоду. |
| opening_balance | decimal | Вхідний залишок. |
| closing_balance | decimal | Вихідний залишок. |
| source_file_id | UUID / FK | Посилання на завантажений файл. |
| template_id | FK | Використаний шаблон імпорту. |
| parser_code | string | Код використаного парсера. |
| parser_version | string | Версія парсера. |
| status | enum | DRAFT / PREVIEW / VALIDATED / IMPORTED / ERROR. |
| rows_total | integer | Загальна кількість рядків. |
| rows_ok | integer | Кількість коректних рядків. |
| rows_warning | integer | Кількість рядків з попередженнями. |
| rows_error | integer | Кількість рядків з помилками. |
| created_at | datetime | Дата створення. |
| created_by | user_id | Користувач. |
| imported_at | datetime | Дата остаточного імпорту. |
| imported_by | user_id | Користувач, який підтвердив імпорт. |
7.2. Об’єкт BankStatementImportRow
Це рядок імпорту банківської виписки.
| Поле | Тип | Опис |
|---|---|---|
| id | UUID / bigint | Ідентифікатор рядка. |
| statement_import_id | FK | Посилання на імпорт. |
| row_number | integer | Номер рядка у файлі. |
| operation_date | datetime | Дата операції. |
| document_date | datetime | Дата документа. |
| value_date | date | Дата валютування. |
| document_number | string | Номер документа. |
| direction | enum | DEBIT / CREDIT. |
| amount | decimal | Сума операції. |
| currency | string | Валюта. |
| payer_name | string | Платник. |
| payer_edrpou | string | ЄДРПОУ платника. |
| payer_iban | string | IBAN платника. |
| payer_mfo | string | МФО платника. |
| receiver_name | string | Отримувач. |
| receiver_edrpou | string | ЄДРПОУ отримувача. |
| receiver_iban | string | IBAN отримувача. |
| receiver_mfo | string | МФО отримувача. |
| counterparty_name | string | Контрагент. |
| counterparty_edrpou | string | ЄДРПОУ контрагента. |
| counterparty_iban | string | IBAN контрагента. |
| counterparty_mfo | string | МФО контрагента. |
| purpose | text | Призначення платежу. |
| uetr | string | Унікальний ідентифікатор платежу. |
| external_transaction_id | string | Ідентифікатор операції у банку або API. |
| raw_row_json | json | Оригінальні дані рядка. |
| hash_key | string | Ключ дедублікації. |
| validation_status | enum | OK / WARNING / ERROR. |
| import_status | enum | NEW / IMPORTED / SKIPPED / ERROR. |
| error_message | text | Повідомлення про помилку. |
| warning_message | text | Повідомлення з попередженням. |
| created_doc_id | FK | Посилання на створений документ або операцію K2 ERP. |
8. Таблиці K2 ERP для модуля
8.1. Таблиця k2_bank_import
| Поле | Призначення |
|---|---|
| id | Ідентифікатор імпорту. |
| org_id | Організація. |
| account_id | Банківський рахунок K2 ERP. |
| bank_id | Банк. |
| file_id | Завантажений файл. |
| template_id | Використаний шаблон. |
| status | Статус імпорту. |
| rows_total | Загальна кількість рядків. |
| rows_ok | Кількість коректних рядків. |
| rows_warning | Кількість рядків з попередженнями. |
| rows_error | Кількість рядків з помилками. |
| created_at | Дата створення. |
| created_by | Користувач. |
| imported_at | Дата імпорту. |
| imported_by | Користувач, який підтвердив імпорт. |
8.2. Таблиця k2_bank_import_row
| Поле | Призначення |
|---|---|
| id | Ідентифікатор рядка. |
| import_id | Посилання на імпорт. |
| row_number | Номер рядка у файлі. |
| operation_date | Дата операції. |
| document_number | Номер документа. |
| direction | DEBIT / CREDIT. |
| amount | Сума. |
| currency | Валюта. |
| counterparty_name | Контрагент. |
| counterparty_edrpou | ЄДРПОУ контрагента. |
| counterparty_iban | IBAN контрагента. |
| purpose | Призначення платежу. |
| uetr | UETR. |
| raw_row_json | Оригінальний рядок. |
| hash_key | Ключ дедублікації. |
| status | OK / WARNING / ERROR / IMPORTED / SKIPPED. |
| error_message | Текст помилки. |
| created_doc_id | Створений документ K2 ERP. |
8.3. Таблиця k2_bank_import_template
| Поле | Призначення |
|---|---|
| id | Ідентифікатор шаблону. |
| name | Назва шаблону. |
| code | Технічний код шаблону. |
| bank_id | Банк або NULL для універсального шаблону. |
| file_type | CSV / DBF / XLS / XLSX / XML / JSON / TXT. |
| encoding | Кодування. |
| delimiter | Роздільник. |
| header_row | Рядок заголовків. |
| mapping_json | Мапінг колонок. |
| rules_json | Правила обробки. |
| signature_json | Сигнатура автоматичного визначення. |
| parser_code | Код парсера. |
| is_system | Системний шаблон. |
| is_active | Активність. |
| version | Версія. |
| created_at | Дата створення. |
| created_by | Автор. |
| updated_at | Дата зміни. |
| updated_by | Автор зміни. |
8.4. Таблиця k2_bank_import_log
| Поле | Призначення |
|---|---|
| id | Ідентифікатор запису журналу. |
| import_id | Посилання на імпорт. |
| event_type | Тип події. |
| event_message | Повідомлення. |
| event_data_json | Додаткові дані. |
| created_at | Дата події. |
| created_by | Користувач або система. |
9. Реєстр шаблонів імпорту
9.1. Призначення шаблонів
Шаблон імпорту описує, як саме потрібно читати конкретний формат банківської виписки.
Шаблон має дозволити налаштувати імпорт без зміни Python-коду.
9.2. Дані шаблону
Шаблон має зберігати:
- назву шаблону;
- банк;
- тип файлу;
- кодування;
- роздільник;
- номер рядка заголовків;
- список обов’язкових колонок;
- список необов’язкових колонок;
- відповідність колонок полям K2 ERP;
- правила визначення дебету/кредиту;
- правила визначення суми;
- правила визначення контрагента;
- правила перетворення дат;
- правила очищення тексту;
- сигнатуру автоматичного розпізнавання.
9.3. Приклад шаблону CSV
{
"code": "CSV_DATA_VYP_MFO_AC_OKPO_V1",
"name": "CSV з DATA_VYP/MFO/AC/OKPO",
"file_type": "CSV",
"encoding": "CP1251",
"delimiter": ";",
"header_row": 1,
"required_columns": [
"DATA_VYP",
"MFO",
"AC",
"OKPO",
"NAME",
"ND",
"DATA_D",
"DK",
"PURPOSE"
],
"optional_columns": [
"DB_IBAN",
"CR_IBAN",
"UETR"
],
"mapping": {
"operation_date": "DATA_D",
"document_number": "ND",
"counterparty_name": "NAME_KOR",
"counterparty_edrpou": "OKPO_KOR",
"purpose": "PURPOSE",
"debit_amount": "DB_SUM_NOM",
"credit_amount": "CR_SUM_NOM",
"debit_iban": "DB_IBAN",
"credit_iban": "CR_IBAN",
"uetr": "UETR"
},
"direction_rule": {
"type": "field_value",
"field": "DK",
"debit_values": ["0", "D", "DB"],
"credit_values": ["1", "C", "CR"]
},
"amount_rule": {
"type": "debit_credit_columns",
"debit_column": "DB_SUM_NOM",
"credit_column": "CR_SUM_NOM",
"fallback_amount_column": "SUM_PD_NOM"
}
}
10. Визначення формату файлу
10.1. Що має визначати система
Після завантаження файлу система має автоматично визначити:
- тип файлу;
- кодування;
- роздільник;
- наявність заголовка;
- номер рядка з заголовками;
- список колонок;
- кількість колонок;
- формат дат;
- наявність колонок дебету/кредиту;
- наявність IBAN;
- наявність ЄДРПОУ/ОКПО;
- наявність UETR;
- можливий шаблон банку.
10.2. Приклад функції визначення метаданих
def detect_file_metadata(file_info):
"""
Визначає базові технічні параметри файлу банківської виписки.
"""
return {
"file_type": "CSV",
"encoding": "CP1251",
"delimiter": ";",
"has_header": True,
"header_row": 1,
"columns": [
"DATA_VYP",
"MFO",
"AC",
"OKPO",
"NAME",
"ND",
"DATA_D",
"DK",
"PURPOSE",
"DB_IBAN",
"CR_IBAN",
"UETR"
]
}
10.3. Логіка автоматичного визначення шаблону
Система має розраховувати коефіцієнт відповідності шаблону.
| Критерій | Вага |
|---|---|
| Збіг обов’язкових колонок | 50% |
| Збіг необов’язкових колонок | 15% |
| Збіг кодування | 10% |
| Збіг роздільника | 10% |
| Збіг формату дат | 10% |
| Збіг специфічних ознак банку | 5% |
Правила:
| Score | Дія |
|---|---|
| >= 85% | Автоматично застосувати шаблон. |
| 60–84% | Запропонувати користувачу підтвердити шаблон. |
| < 60% | Відкрити майстер ручного мапінгу. |
11. Python-реєстр парсерів
11.1. Загальний підхід
Замість Java-подібного interface доцільно використовувати простий реєстр Python-функцій або класів.
PARSERS = {
"CSV_GENERIC": {
"detect": detect_generic_csv,
"parse": parse_generic_csv
},
"CSV_DATA_VYP_MFO_AC_OKPO": {
"detect": detect_csv_data_vyp,
"parse": parse_csv_data_vyp
},
"DBF_GENERIC": {
"detect": detect_generic_dbf,
"parse": parse_generic_dbf
},
"XLSX_GENERIC": {
"detect": detect_generic_xlsx,
"parse": parse_generic_xlsx
},
"ISO20022_CAMT053": {
"detect": detect_camt053,
"parse": parse_camt053
}
}
11.2. Вимоги до парсера
Кожен парсер має:
- перевіряти, чи може він обробити файл;
- читати дані файлу;
- перетворювати рядки у внутрішній формат;
- не створювати документи K2 ERP напряму;
- повертати структурований результат;
- повертати помилки без аварійного завершення;
- зберігати raw-дані рядків.
11.3. Універсальний формат рядка після парсингу
Кожен парсер, незалежно від типу файлу, має повертати рядки в єдиному форматі:
{
"row_number": 1,
"operation_date": "2026-05-25",
"document_date": "2026-05-25",
"document_number": "12345",
"direction": "CREDIT",
"amount": 1500.00,
"currency": "UAH",
"payer_name": "ТОВ Приклад",
"payer_edrpou": "12345678",
"payer_iban": "UA123456789000000000000000001",
"receiver_name": "ТОВ Отримувач",
"receiver_edrpou": "87654321",
"receiver_iban": "UA123456789000000000000000002",
"counterparty_name": "ТОВ Приклад",
"counterparty_edrpou": "12345678",
"counterparty_iban": "UA123456789000000000000000001",
"purpose": "Оплата за товар згідно рахунку",
"uetr": "",
"raw_row": {}
}
12. Головний сценарій Python-обробки
12.1. Підготовка виписки до імпорту
def import_bank_statement(k2, file_id, account_id, template_id=None):
"""
Головна функція підготовки банківської виписки до імпорту.
k2 - об'єкт доступу до середовища K2 ERP
file_id - ідентифікатор завантаженого файлу
account_id - банківський рахунок у K2 ERP
template_id - шаблон імпорту, якщо користувач вибрав його вручну
"""
file_info = load_uploaded_file(k2, file_id)
file_meta = detect_file_metadata(file_info)
if template_id:
template = load_import_template(k2, template_id)
else:
template = detect_import_template(k2, file_meta)
if not template:
return {
"status": "need_mapping",
"message": "Формат виписки не розпізнано. Потрібне ручне налаштування мапінгу.",
"file_meta": file_meta
}
raw_rows = read_statement_rows(file_info, file_meta, template)
mapped_rows = apply_mapping(raw_rows, template)
validation_result = validate_statement_rows(k2, mapped_rows, account_id)
import_id = save_import_preview(
k2=k2,
file_id=file_id,
account_id=account_id,
template=template,
rows=validation_result["rows"]
)
return {
"status": "preview",
"import_id": import_id,
"message": "Виписку підготовлено до імпорту",
"rows_count": len(validation_result["rows"]),
"errors_count": validation_result["errors_count"],
"warnings_count": validation_result["warnings_count"]
}
12.2. Остаточне підтвердження імпорту
def commit_bank_statement_import(k2, import_id, user_id):
"""
Остаточне створення банківських операцій у K2 ERP
на основі попередньо підготовленого імпорту.
"""
import_header = load_import_header(k2, import_id)
rows = load_import_rows(k2, import_id)
created_count = 0
skipped_count = 0
error_count = 0
for row in rows:
if row["status"] == "ERROR":
error_count += 1
continue
if is_duplicate(k2, row):
mark_row_skipped(k2, row["id"], "Дублікат операції")
skipped_count += 1
continue
try:
doc_id = create_bank_operation_in_k2(k2, import_header, row)
mark_row_imported(k2, row["id"], doc_id)
created_count += 1
except Exception as exc:
mark_row_error(k2, row["id"], str(exc))
error_count += 1
update_import_status(
k2=k2,
import_id=import_id,
user_id=user_id,
created_count=created_count,
skipped_count=skipped_count,
error_count=error_count
)
return {
"status": "done",
"created_count": created_count,
"skipped_count": skipped_count,
"error_count": error_count
}
13. Приклади банківських виписок і принцип їх обробки
13.1. CSV з колонками DATA_VYP / MFO / AC / OKPO
Приклад структури заголовка:
DATA_VYP;MFO;AC;OKPO;NAME;ND;DATA_D;DK;MFO_KOR;AC_KOR;OKPO_KOR;NAME_KOR;CUR_TAG;CUR_CODE;CUR_RATE;AC_CUR_TAG;AccountCur;SUM_PD_NOM;SUM_PD_EQ;PURPOSE;IN_RST_NO;IN_RST_EQ;OUT_RST_NO;OUT_RST_EQ;DB_SUM_NOM;CR_SUM_NOM;DB_SUM_EQ;CR_SUM_EQ;DAT_OST_OB;DB_IBAN;CR_IBAN;NAME_ACT;OKPO_ACT;Doc_ACT;ResC_ACT;NAME_KOR_ACT;OKPO_KOR_ACT;Doc_KOR_ACT;ResC_KOR_ACT;ADD_INFO;UETR
Характеристики:
| Параметр | Значення |
|---|---|
| Тип файлу | CSV |
| Роздільник | ; |
| Кодування | Windows-1251 / CP1251 |
| Кількість колонок | 41 |
| Ознака дебет/кредит | DK або колонки DB_SUM_NOM / CR_SUM_NOM |
| Призначення платежу | PURPOSE |
| IBAN | DB_IBAN / CR_IBAN |
| Унікальний ідентифікатор | UETR |
Мапінг:
| Поле CSV | Поле K2 ERP |
|---|---|
| DATA_VYP | statement_date |
| MFO | own_bank_mfo |
| AC | own_account_number_legacy |
| OKPO | own_edrpou |
| NAME | own_account_name |
| ND | document_number |
| DATA_D | operation_date |
| DK | direction |
| MFO_KOR | counterparty_mfo |
| AC_KOR | counterparty_account_legacy |
| OKPO_KOR | counterparty_edrpou |
| NAME_KOR | counterparty_name |
| SUM_PD_NOM | amount |
| PURPOSE | purpose |
| DB_SUM_NOM | debit_amount |
| CR_SUM_NOM | credit_amount |
| DB_IBAN | debit_iban |
| CR_IBAN | credit_iban |
| UETR | uetr |
Принцип обробки:
- визначити кодування CP1251;
- визначити роздільник `;`;
- прочитати заголовок;
- перевірити наявність колонок DATA_VYP, MFO, AC, OKPO, ND, DATA_D, DK;
- застосувати шаблон CSV_DATA_VYP_MFO_AC_OKPO_V1;
- визначити напрям за DK або за ненульовими колонками DB_SUM_NOM / CR_SUM_NOM;
- визначити контрагента відносно власного рахунку;
- сформувати внутрішні рядки BankStatementImportRow.
Приклад парсера:
def detect_csv_data_vyp(file_meta):
required_columns = {
"DATA_VYP",
"MFO",
"AC",
"OKPO",
"ND",
"DATA_D",
"DK",
"PURPOSE"
}
columns = set(file_meta.get("columns", []))
return required_columns.issubset(columns)
def parse_csv_data_vyp(rows, template):
result = []
for row_number, row in enumerate(rows, start=1):
debit_amount = parse_amount(row.get("DB_SUM_NOM"))
credit_amount = parse_amount(row.get("CR_SUM_NOM"))
if debit_amount > 0:
direction = "DEBIT"
amount = debit_amount
elif credit_amount > 0:
direction = "CREDIT"
amount = credit_amount
else:
direction = detect_direction_by_dk(row.get("DK"))
amount = parse_amount(row.get("SUM_PD_NOM"))
result.append({
"row_number": row_number,
"operation_date": parse_date(row.get("DATA_D")),
"document_date": parse_date(row.get("DATA_D")),
"document_number": row.get("ND"),
"direction": direction,
"amount": amount,
"currency": row.get("AccountCur") or "UAH",
"counterparty_name": row.get("NAME_KOR"),
"counterparty_edrpou": row.get("OKPO_KOR"),
"counterparty_iban": detect_counterparty_iban(row, direction),
"purpose": row.get("PURPOSE"),
"uetr": row.get("UETR"),
"raw_row": row
})
return result
13.2. ПриватБанк CSV/DBF/API
Для ПриватБанку потрібно передбачити кілька каналів імпорту:
- CSV-файл;
- DBF-файл;
- API / Автоклієнт.
Очікувані парсери:
- PrivatbankCsvParser;
- PrivatbankDbfParser;
- PrivatbankApiParser.
Принцип обробки CSV/DBF:
- визначити тип файлу;
- прочитати заголовки або DBF-структуру;
- визначити, чи є це шаблоном ПриватБанку;
- застосувати відповідний парсер або шаблон мапінгу;
- привести дані до BankStatementImportRow.
Принцип обробки API:
- користувач налаштовує API-доступ;
- система отримує список рахунків;
- користувач обирає рахунок;
- система запитує виписку за період;
- відповідь API перетворюється на внутрішній формат K2 ERP;
- дані записуються у preview;
- користувач підтверджує імпорт.
13.3. ISO 20022 camt.053 XML
ISO 20022 camt.053 має підтримуватися як стандартизований XML-формат банківської виписки.
Очікуваний парсер:
- Iso20022Camt053Parser.
Основні вузли для обробки:
| XML-вузол | Поле K2 ERP |
|---|---|
| GrpHdr/MsgId | Зовнішній ідентифікатор виписки. |
| GrpHdr/CreDtTm | Дата створення виписки. |
| Stmt/Acct/Id/IBAN | IBAN рахунку. |
| Stmt/Bal | Залишки. |
| Ntry/Amt | Сума операції. |
| Ntry/CdtDbtInd | Напрям DEBIT/CREDIT. |
| Ntry/BookgDt | Дата бухгалтерського проведення. |
| Ntry/ValDt | Дата валютування. |
| Ntry/NtryDtls/TxDtls/RmtInf/Ustrd | Призначення платежу. |
| Ntry/NtryDtls/TxDtls/Refs | Референси платежу. |
| Ntry/NtryDtls/TxDtls/RltdPties | Платник / отримувач. |
Особливості:
- один XML може містити кілька виписок;
- один XML може містити кілька рахунків;
- призначення платежу може бути структурованим або неструктурованим;
- дані контрагентів можуть бути вкладені глибоко в XML;
- необхідно підтримати namespaces XML.
13.4. monobank API
Для monobank потрібно передбачити API-імпорт.
Очікуваний парсер:
- MonobankApiStatementParser.
Типовий мапінг:
| Поле API | Поле K2 ERP |
|---|---|
| id | external_transaction_id |
| time | operation_date |
| description | purpose |
| mcc | mcc_code |
| amount | amount |
| operationAmount | operation_amount |
| currencyCode | currency_code |
| balance | balance_after |
| counterEdrpou | counterparty_edrpou |
| counterIban | counterparty_iban |
Особливість:
Суми можуть передаватися в мінорних одиницях валюти, наприклад у копійках.
amount = amount_from_api / 100
13.5. iFOBS / клієнт-банк
Для клієнт-банківських систем типу iFOBS потрібно передбачити кілька форматів:
- DBF;
- альтернативний DBF;
- XLS;
- XML;
- TXT;
- SAP-подібні формати.
Очікувані парсери:
- IFobsDbfParser;
- IFobsAlternativeDbfParser;
- IFobsXlsParser;
- IFobsXmlParser;
- IFobsTxtParser.
Важливо:
банк ≠ формат
банк = організація
формат = структура файлу
платформа = клієнт-банк
Один банк може мати кілька форматів, а одна клієнт-банківська платформа може використовуватися різними банками.
14. Майстер налаштування імпорту
14.1. Призначення
Майстер налаштування імпорту потрібен для випадків, коли система не змогла автоматично розпізнати формат виписки або користувач хоче створити власний шаблон.
14.2. Кроки майстра
- Завантаження файлу.
- Вибір типу файлу, якщо система не визначила його автоматично.
- Вибір кодування.
- Вибір роздільника.
- Вибір аркуша для Excel.
- Вибір рядка заголовків.
- Попередній перегляд перших рядків.
- Мапінг колонок.
- Налаштування правила суми.
- Налаштування правила дебет/кредит.
- Налаштування правила контрагента.
- Налаштування формату дат.
- Тестове розпізнавання.
- Збереження шаблону.
- Імпорт.
14.3. Мапінг колонок
| Поле K2 ERP | Колонка у файлі | Обов’язковість |
|---|---|---|
| Дата операції | DATA_D | Так |
| Номер документа | ND | Так |
| Сума операції | SUM_PD_NOM | Так |
| Дебет | DB_SUM_NOM | Ні |
| Кредит | CR_SUM_NOM | Ні |
| Контрагент | NAME_KOR | Так |
| ЄДРПОУ контрагента | OKPO_KOR | Ні |
| IBAN дебету | DB_IBAN | Ні |
| IBAN кредиту | CR_IBAN | Ні |
| Призначення платежу | PURPOSE | Так |
| UETR | UETR | Ні |
14.4. Правила суми
Майстер має підтримувати такі варіанти:
| Варіант | Приклад |
|---|---|
| Одна колонка суми зі знаком | amount = -1500.00 або 1500.00 |
| Окрема колонка дебету та кредиту | debit_amount / credit_amount |
| Окрема колонка суми та колонка напряму | amount + DK |
| Сума в копійках | amount / 100 |
| Сума з комою | 1 500,25 |
14.5. Правила дат
Майстер має підтримувати такі формати дат:
- DD.MM.YYYY;
- DD-MM-YYYY;
- YYYY-MM-DD;
- DD.MM.YYYY HH:mm:ss;
- Unix timestamp;
- ISO datetime.
15. Валідація
15.1. Обов’язкові перевірки
| Перевірка | Рівень |
|---|---|
| Немає дати операції | ERROR |
| Немає суми | ERROR |
| Немає напряму платежу | ERROR |
| Немає призначення платежу | WARNING |
| Невалідний IBAN | WARNING |
| Невалідний ЄДРПОУ | WARNING |
| Дублікат операції | WARNING / SKIP |
| Невідома валюта | ERROR |
| Невідомий рахунок K2 ERP | ERROR |
| Період виписки перетинається з уже імпортованим | WARNING |
15.2. Приклад функції валідації
def validate_statement_rows(k2, rows, account_id):
errors_count = 0
warnings_count = 0
validated_rows = []
for row in rows:
messages = []
status = "OK"
if not row.get("operation_date"):
status = "ERROR"
messages.append("Не заповнена дата операції")
if not row.get("amount"):
status = "ERROR"
messages.append("Не заповнена сума операції")
if not row.get("direction"):
status = "ERROR"
messages.append("Не визначено напрям операції")
if not row.get("purpose"):
if status != "ERROR":
status = "WARNING"
messages.append("Не заповнено призначення платежу")
if row.get("counterparty_iban") and not is_valid_iban(row["counterparty_iban"]):
if status != "ERROR":
status = "WARNING"
messages.append("Некоректний IBAN контрагента")
row["validation_status"] = status
row["validation_messages"] = messages
if status == "ERROR":
errors_count += 1
elif status == "WARNING":
warnings_count += 1
validated_rows.append(row)
return {
"rows": validated_rows,
"errors_count": errors_count,
"warnings_count": warnings_count
}
16. Дедублікація
16.1. Принцип
Для кожної операції система формує hash_key.
Якщо у виписці є UETR, він має використовуватись як основа для дедублікації.
Якщо UETR відсутній, ключ формується з комбінації основних полів.
16.2. Правило формування hash_key
def build_hash_key(row, account_iban):
if row.get("uetr"):
return sha256_string(
account_iban + "|" +
row.get("uetr")
)
return sha256_string(
account_iban + "|" +
str(row.get("operation_date")) + "|" +
str(row.get("document_number")) + "|" +
str(row.get("amount")) + "|" +
str(row.get("direction")) + "|" +
str(row.get("counterparty_iban")) + "|" +
str(row.get("purpose"))
)
16.3. Поведінка при дублікатах
| Ситуація | Дія |
|---|---|
| Операція вже імпортована | Позначити як SKIPPED. |
| Операція є у preview цього ж імпорту | Позначити як WARNING. |
| Операція схожа, але hash_key відрізняється | Показати користувачу як можливий дубль. |
17. Попередній перегляд
17.1. Мета preview
До створення реальних документів K2 ERP користувач має бачити, що саме буде імпортовано.
17.2. Поля preview
| Колонка | Опис |
|---|---|
| Рядок | Номер рядка у файлі. |
| Дата | Дата операції. |
| Документ | Номер документа. |
| Дебет | Сума списання. |
| Кредит | Сума надходження. |
| Контрагент | Назва контрагента. |
| ЄДРПОУ | Код контрагента. |
| IBAN | Рахунок контрагента. |
| Призначення | Призначення платежу. |
| Статус | OK / WARNING / ERROR / DUPLICATE. |
| Повідомлення | Опис помилки або попередження. |
17.3. Дії користувача у preview
Користувач має мати можливість:
- переглянути всі рядки;
- відфільтрувати рядки з помилками;
- відфільтрувати дублікати;
- виключити рядок з імпорту;
- повернутися до мапінгу;
- підтвердити імпорт;
- скасувати імпорт.
18. Імпорт у K2 ERP
Після підтвердження користувачем система має:
- створити або оновити запис заголовка імпорту;
- створити або оновити рядки імпорту;
- перевірити дублікати;
- створити банківські операції K2 ERP або документи руху коштів;
- зв’язати створені документи з рядками імпорту;
- зберегти статус кожного рядка;
- записати дію в журнал аудиту.
19. UI-вимоги
19.1. Форма імпорту
Форма імпорту має містити:
- організацію;
- банківський рахунок;
- банк;
- поле вибору файлу;
- поле вибору шаблону;
- кнопку “Визначити формат”;
- кнопку “Налаштувати мапінг”;
- кнопку “Попередній перегляд”;
- кнопку “Імпортувати”;
- статус обробки;
- повідомлення про помилки.
19.2. Форма шаблонів
Форма шаблонів має дозволяти:
- створювати шаблон;
- редагувати шаблон;
- копіювати шаблон;
- вимикати шаблон;
- переглядати JSON-мапінг;
- тестувати шаблон на файлі;
- бачити історію змін.
19.3. Форма журналу імпорту
Журнал імпорту має показувати:
- дату імпорту;
- користувача;
- банк;
- рахунок;
- файл;
- шаблон;
- кількість рядків;
- кількість помилок;
- кількість дублікатів;
- статус імпорту.
20. Права доступу
| Роль | Права |
|---|---|
| Accountant | Завантаження виписок, preview, запуск імпорту. |
| Chief Accountant | Підтвердження імпорту, створення шаблонів. |
| Admin | Налаштування системних шаблонів і парсерів. |
| Auditor | Перегляд історії імпорту. |
21. Логування та аудит
Система має зберігати:
- користувача, який завантажив файл;
- дату й час завантаження;
- назву файлу;
- хеш файлу;
- розмір файлу;
- розпізнаний формат;
- використаний шаблон;
- версію парсера;
- кількість рядків;
- кількість успішних рядків;
- кількість рядків з помилками;
- кількість пропущених дублікатів;
- оригінальний файл;
- raw-дані кожного рядка;
- дату остаточного імпорту;
- користувача, який підтвердив імпорт.
22. Безпека
22.1. Файли
Модуль має:
- обмежувати допустимі типи файлів;
- перевіряти розмір файлу;
- забороняти виконання макросів Excel;
- зберігати оригінальний файл у захищеному сховищі;
- обмежувати доступ до файлів за ролями;
- не дозволяти виконання вкладених скриптів або формул.
22.2. API-ключі
Для API банків потрібно:
- зберігати токени в зашифрованому вигляді;
- не показувати токен після збереження;
- не логувати секрети;
- підтримувати відкликання токена;
- фіксувати дату останнього успішного запиту.
23. MVP першої версії
У першій версії необхідно реалізувати:
| Компонент | Пріоритет |
|---|---|
| Generic CSV Mapper | P1 |
| Generic DBF Mapper | P1 |
| Generic XLSX Mapper | P1 |
| ISO 20022 camt.053 Parser | P1 |
| CSV-шаблон DATA_VYP/MFO/AC/OKPO | P1 |
| ПриватБанк CSV/DBF | P1 |
| ПриватБанк API | P2 |
| monobank API | P2 |
| iFOBS DBF/XLS/XML | P2 |
24. Тестові сценарії
24.1. Імпорт розпізнаного CSV
Дано: CSV-файл з колонками DATA_VYP, MFO, AC, OKPO, DATA_D, DK, PURPOSE.
Очікуваний результат:
- система визначає формат автоматично;
- система визначає кодування CP1251;
- система визначає роздільник `;`;
- система застосовує відповідний шаблон;
- система створює preview;
- система імпортує рядки після підтвердження.
24.2. Імпорт невідомого CSV
Дано: CSV-файл з невідомими колонками.
Очікуваний результат:
- система не завершується з помилкою;
- відкривається майстер мапінгу;
- користувач налаштовує відповідність колонок;
- система зберігає шаблон;
- наступний файл такої структури розпізнається автоматично.
24.3. Імпорт camt.053 XML
Дано: XML-файл ISO 20022 camt.053.
Очікуваний результат:
- система читає рахунок;
- система читає залишки;
- система читає операції;
- система визначає дебет/кредит;
- система читає призначення платежу;
- система створює операції K2 ERP.
24.4. Повторний імпорт того самого файлу
Очікуваний результат:
- система визначає дублікати;
- дублікати не імпортуються повторно;
- користувач бачить список пропущених рядків.
24.5. Некоректний IBAN
Очікуваний результат:
- рядок не блокується повністю, якщо IBAN не обов’язковий;
- система показує warning;
- користувач може імпортувати після підтвердження.
25. Критерії приймання
Модуль вважається реалізованим, якщо:
- підтримує імпорт CSV, DBF, XLSX, XML camt.053;
- автоматично визначає кодування CSV;
- автоматично визначає роздільник CSV;
- має реєстр Python-парсерів;
- має майстер ручного мапінгу;
- дозволяє зберігати шаблони мапінгу;
- має preview перед імпортом;
- виконує валідацію обов’язкових полів;
- виконує дедублікацію;
- зберігає оригінальний файл;
- зберігає raw-дані рядків;
- має журнал імпорту;
- підтримує мінімум один реальний CSV-шаблон;
- підтримує ISO 20022 camt.053;
- дозволяє додати новий CSV/XLSX-шаблон без зміни програмного коду;
- не створює документи K2 ERP до підтвердження користувачем;
- коректно обробляє помилки без аварійного завершення процесу.
26. Обмеження та припущення
- Модуль не гарантує автоматичне розпізнавання 100% банківських форматів без первинного налаштування.
- Для нового невідомого формату користувач або адміністратор має один раз створити шаблон мапінгу.
- API банків реалізуються окремо для кожного банку, оскільки єдиного API для всіх банків немає.
- ISO 20022 camt.053 підтримується як найбільш стандартизований XML-формат, але не всі банки можуть надавати його клієнтам.
- CSV, DBF та XLSX є лише контейнерами даних, а не єдиним стандартом структури банківської виписки.
27. Подальший розвиток
У наступних версіях модуля можна реалізувати:
- автоматичне навчання на завантажених шаблонах;
- бібліотеку шаблонів українських банків;
- експорт і імпорт шаблонів між інсталяціями K2 ERP;
- підключення API інших банків;
- автоматичне звіряння платежів з рахунками, актами та договорами;
- автоматичне визначення статей руху коштів;
- автоматичне визначення контрагентів;
- правила автопроведення банківських операцій;
- контроль розривів у виписках за періодами.
28. Головний висновок
Модуль має бути не окремим імпортом ПриватБанку, Укргазбанку чи іншого банку, а універсальною платформою імпорту банківських виписок.
Правильна модель:
1. Стандартний формат:
ISO 20022 camt.053
2. Готові адаптери:
ПриватБанк, monobank, iFOBS, CSV/DBF конкретних банків
3. Гнучкий мапінг:
будь-який CSV / DBF / XLSX без зміни коду
4. K2 ERP Python-реалізація:
форма K2 ERP → Python-скрипт → таблиці preview → підтвердження → документи K2 ERP
Саме така архітектура дозволить K2 ERP підтримувати поточні та майбутні формати банківських виписок українських банків без постійного переписування програмного коду.