Перехід з іншого застосунку (з допомогою ШІ)
Копійка імпортує лише одне: власний формат резервної копії, один файл JSON. Імпорту з експортів інших застосунків навмисно немає — кожен застосунок експортує щось своє, і конвертер, правильний для одного експорту, хибний для наступного. Натомість працює ось що: експортуйте дані зі старого застосунку, передайте цей файл і запит нижче ШІ-асистентові (Claude, ChatGPT, локальній моделі — будь-чому, що вміє прочитати файл і написати JSON) та імпортуйте результат.
Усе займає кілька хвилин. Ваші дані потрапляють туди, куди ви їх надсилаєте, і більше нікуди; якщо для вас це важливо, скористайтеся локальною моделлю або попросіть асистента написати скрипт і запустіть його самі.
Кроки
- Експортуйте дані зі старого застосунку. Найкраще CSV; JSON чи XLSX теж підійдуть. Вам потрібні операції з датою, сумою, валютою, рахунком, категорією і (якщо є) нотатками, тегами й переказами.
- Відкрийте чат із ШІ-асистентом, прикріпіть експорт і вставте запит із наступного розділу. Прочитайте його запитання — зазвичай йому треба знати вашу базову валюту і те, як у файлі позначено перекази.
- Збережіть відповідь як
kopiyka-import.json(асистент може запропонувати файл; якщо він виводить JSON текстом, скопіюйте все між зовнішніми фігурними дужками). - Перенесіть файл на телефон — AirDrop, iCloud Drive, «Файли».
- У Копійці: Налаштування → Керування даними → Імпорт → Замінити все файлом (на щойно встановленому застосунку) або Обʼєднати (щоб додати до того, що вже є). Перед заміною застосунок записує страхувальну резервну копію і повідомляє, де вона.
- Перегляньте кілька місяців в «Операціях» і залишки рахунків у «Налаштування → Рахунки». Якщо
залишок не збігається, в експорті не було початкового залишку: задайте
opening_balance_minorдля цього рахунку в JSON (або відредагуйте рахунок у застосунку), щоб сьогоднішній залишок збігався з банківським.
Запит
Скопіюйте весь цей блок. Прикріпіть експорт у тому самому повідомленні.
Я переношу історію своїх особистих фінансів у застосунок під назвою Kopiyka. Він імпортує один
формат файлу, описаний нижче. Будь ласка, перетвори прикріплений експорт у цей формат.
Перш ніж щось писати, запитай мене (одним повідомленням) про все, чого не можеш вивести з файлу:
мою базову валюту, як у експорті позначено перекази між моїми рахунками, чи є категорії вкладеними
(папка › категорія), і який часовий пояс використовувати, якщо дати не мають зсуву.
Потім створи ОДИН документ JSON, який точно відповідає специфікації. Правила:
- Збережи кожну операцію. Не підсумовуй, не вибирай частину і не відкидай рядків. Якщо рядок не
вдається перетворити, перелічи його в кінці відповіді, а не пропускай мовчки.
- Гроші — цілі числа в мінорних одиницях валюти *рахунку* (центи, гроші, копійки):
12.34 EUR → 1234. Витрати відʼємні, доходи додатні.
- Кожному рядку потрібен `id`: використовуй новий UUID v4 для кожного рядка й той самий id усюди,
де на цей рядок є посилання (account_id, category_id, parent_id, tag_ids). Ніколи не виводь id з назви.
- Категорії: один рядок на категорію. Папка — це просто категорія, у якої `parent_id` дорівнює null
і на яку посилаються інші категорії. Операції мають посилатися на найглибшу категорію, ніколи не
на папку. Якщо в експорті плаский список категорій, зроби їх усі верхнього рівня.
- Теги: один рядок на кожен окремий тег; `tag_ids` операції — це *рядок* із JSON-масивом id тегів,
наприклад "[\"…\",\"…\"]" (порожній список — "[]").
- Перекази: в експорті зазвичай або один рядок із рахунком-джерелом і рахунком-отримувачем, або два
рядки (гроші вийшли, гроші прийшли). Створи дві операції — відʼємну на рахунку-джерелі й додатну на
рахунку-отримувачі, кожну у валюті свого рахунку, — зі спільним новим `transfer_id` і
`category_id` null. Якщо дві частини в різних валютах, задай `entered_amount_minor`,
`entered_currency` і `exchange_rate` для частини отримувача, як описано нижче.
- Дати — ISO 8601 зі зсувом: "2026-03-17T16:28:39+01:00". Якщо в експорті є лише день, використовуй
12:00:00 у часовому поясі, який я назвав. `updated_at` — епоха в мілісекундах: використовуй час
експорту (зараз) для кожного рядка. `deleted` завжди 0.
- Регулярні правила, бюджети, борги й аналітика необовʼязкові. Додавай регулярні правила, лише якщо
в експорті вони явно є (підписки з періодом); інакше залиш масиви порожніми, і я налаштую їх у застосунку.
- `settings.base_currency` — моя базова валюта. Залиш `rates` порожнім масивом, якщо в експорті немає
курсів валют.
- Виведи в цьому блоці коду JSON і нічого більше. Форматуй з відступом в 1 пробіл.
=== СПЕЦИФІКАЦІЯ: JSON резервної копії Kopiyka ===
{
"format": "kopiyka-backup", // саме так
"version": 1,
"exported_at": "2026-09-12T10:00:00.000Z",
"settings": { "base_currency": "EUR" },
"accounts": [ Account ],
"categories": [ Category ],
"tags": [ Tag ],
"transactions": [ Transaction ],
"recurring_rules": [ RecurringRule ], // може бути []
"budgets": [], // може бути []
"insights": [], // може бути []
"debts": [], // може бути []
"rates": [] // може бути []
}
Кожен рядок у кожній таблиці має ці три поля:
id: string (UUID v4) updated_at: integer (епоха в мс) deleted: 0
Account {
name: string
currency: string // ISO 4217, "EUR", "USD", "PLN", "UAH" …
type: "cash" | "bank" | "card" | "investment" | "savings" | "other"
group_name: string // заголовок, під яким показано рахунки; "Personal", якщо в експорті його немає
icon: string | null // назва SF Symbol, напр. "creditcard.fill", "banknote.fill", "building.columns.fill"; null теж підходить
color: string | null // "#RRGGBB" або null
sort: integer // порядок показу, 0, 1, 2 …
archived: 0 | 1
include_in_net_worth: 0 | 1 // 1, якщо це не кредитний рахунок чи позика, які ви виключаєте
opening_balance_minor: integer // залишок до першої операції у файлі; 0, якщо невідомо
}
Category {
name: string
parent_id: string | null // id категорії-папки або null для категорії верхнього рівня
icon: string | null // назва SF Symbol або null
color: string | null // "#RRGGBB" або null
sort: integer
kind: "expense" | "income"
description: string | null // необовʼязкові слова, що описують, що сюди належить
}
Tag {
name: string
color: string | null
category_ids: string // рядок із JSON-масивом id категорій, для яких призначено тег; "[]" = будь-яких
}
Transaction {
account_id: string
date: string // ISO 8601 зі зсувом
amount_minor: integer // мінорні одиниці валюти рахунку; відʼємне = витрата
category_id: string | null // найглибша категорія; null для переказів чи рядків без категорії
payee: string | null // магазин чи контрагент, якщо експорт відокремлює його від нотатки
notes: string | null // нотатка / опис
tag_ids: string // рядок із JSON-масивом id тегів, "[]", якщо тегів немає
pending: 0 | 1 // 0 для історії
transfer_id: string | null // той самий новий UUID на обох частинах переказу, інакше null
entered_amount_minor: integer | null // лише коли платіж був в іншій валюті: сума, як її списано
entered_currency: string | null // … і її валюта
exchange_rate: number | null // одиниць введеної валюти за 1 одиницю валюти рахунку (entered_amount / amount)
recurring_id: string | null // id регулярного правила, що створило цей рядок, інакше null
lat: number | null // де це записано, якщо відомо
lon: number | null
place: string | null // назва цього місця для людей
photo: null // фото чеків так імпортувати не можна
source: null
}
RecurringRule {
account_id: string
amount_minor: integer // відʼємне = витрата
category_id: string | null
payee: string | null
notes: string | null
tag_ids: string // "[]", якщо тегів немає
frequency: "daily" | "weekly" | "monthly" | "yearly"
interval: integer // 1 = кожен період, 2 = кожен другий період …
start_date: string // "YYYY-MM-DD"
end_date: string | null
next_date: string // "YYYY-MM-DD", перше ще не записане повторення (після сьогодні)
notify: 0 | 1
notify_days_before: integer
auto_post: 0 | 1 // 1 = записується сам у свій день; 0 = спершу питає
active: 0 | 1
time_of_day: string // "HH:MM"
}
=== ПРИКЛАД (два рахунки, папка з однією категорією, тег, одна витрата й один переказ) ===
{
"format": "kopiyka-backup",
"version": 1,
"exported_at": "2026-09-12T10:00:00.000Z",
"settings": { "base_currency": "EUR" },
"accounts": [
{ "id": "5f1a2b3c-0001-4a00-8000-000000000001", "updated_at": 1757671200000, "deleted": 0,
"name": "Main", "currency": "EUR", "type": "bank", "group_name": "Personal", "icon": "building.columns.fill",
"color": "#2B2B2E", "sort": 0, "archived": 0, "include_in_net_worth": 1, "opening_balance_minor": 250000 },
{ "id": "5f1a2b3c-0001-4a00-8000-000000000002", "updated_at": 1757671200000, "deleted": 0,
"name": "Cash", "currency": "EUR", "type": "cash", "group_name": "Personal", "icon": "banknote.fill",
"color": null, "sort": 1, "archived": 0, "include_in_net_worth": 1, "opening_balance_minor": 0 }
],
"categories": [
{ "id": "5f1a2b3c-0002-4a00-8000-000000000001", "updated_at": 1757671200000, "deleted": 0,
"name": "Food", "parent_id": null, "icon": "cart.fill", "color": "#FF9F0A", "sort": 0, "kind": "expense", "description": null },
{ "id": "5f1a2b3c-0002-4a00-8000-000000000002", "updated_at": 1757671200000, "deleted": 0,
"name": "Groceries", "parent_id": "5f1a2b3c-0002-4a00-8000-000000000001", "icon": "cart.fill", "color": "#FF9F0A",
"sort": 1, "kind": "expense", "description": "supermarket, market, bakery" }
],
"tags": [
{ "id": "5f1a2b3c-0003-4a00-8000-000000000001", "updated_at": 1757671200000, "deleted": 0,
"name": "Weekend", "color": "#30D158", "category_ids": "[]" }
],
"transactions": [
{ "id": "5f1a2b3c-0004-4a00-8000-000000000001", "updated_at": 1757671200000, "deleted": 0,
"account_id": "5f1a2b3c-0001-4a00-8000-000000000001", "date": "2026-09-06T18:42:00+01:00", "amount_minor": -2350,
"category_id": "5f1a2b3c-0002-4a00-8000-000000000002", "payee": "Pingo Doce", "notes": null,
"tag_ids": "[\"5f1a2b3c-0003-4a00-8000-000000000001\"]", "pending": 0, "transfer_id": null,
"entered_amount_minor": null, "entered_currency": null, "exchange_rate": null, "recurring_id": null,
"lat": null, "lon": null, "place": null, "photo": null, "source": null },
{ "id": "5f1a2b3c-0004-4a00-8000-000000000002", "updated_at": 1757671200000, "deleted": 0,
"account_id": "5f1a2b3c-0001-4a00-8000-000000000001", "date": "2026-09-07T09:00:00+01:00", "amount_minor": -10000,
"category_id": null, "payee": null, "notes": "ATM", "tag_ids": "[]", "pending": 0,
"transfer_id": "5f1a2b3c-0005-4a00-8000-000000000001",
"entered_amount_minor": null, "entered_currency": null, "exchange_rate": null, "recurring_id": null,
"lat": null, "lon": null, "place": null, "photo": null, "source": null },
{ "id": "5f1a2b3c-0004-4a00-8000-000000000003", "updated_at": 1757671200000, "deleted": 0,
"account_id": "5f1a2b3c-0001-4a00-8000-000000000002", "date": "2026-09-07T09:00:00+01:00", "amount_minor": 10000,
"category_id": null, "payee": null, "notes": "ATM", "tag_ids": "[]", "pending": 0,
"transfer_id": "5f1a2b3c-0005-4a00-8000-000000000001",
"entered_amount_minor": null, "entered_currency": null, "exchange_rate": null, "recurring_id": null,
"lat": null, "lon": null, "place": null, "photo": null, "source": null }
],
"recurring_rules": [],
"budgets": [],
"insights": [],
"debts": [],
"rates": []
}
Перевірка результату перед імпортом
- Файл має починатися з
"format": "kopiyka-backup"і містити масивaccounts, інакше застосунок відхилить його з помилкою «Not a Kopiyka backup» («Це не резервна копія Копійки»). - Кожен
account_id/category_id/parent_id/ id тегу має існувати у файлі. Під час імпорту застосунок цього не перевіряє: операція, що посилається на невідомий рахунок, ніде не зʼявиться і не врахується в жодному залишку. - Суми мають бути цілими числами.
12.34— неправильно;1234— правильно. - Переказ — це два рядки з однаковим
transfer_idі протилежними знаками. - Якщо ви запускали конвертацію двічі, імпортуйте лише останній файл: id щоразу генеруються заново, тож імпорт обох подвоїть усе. (Якщо ви справді хочете повторно імпортувати виправлену версію, використовуйте Замінити все — обʼєднання ніколи нічого не видаляє, тож старі рядки лишилися б.)
Повний набір правил, яких дотримується імпорт, — id визначає запис, обʼєднання ніколи не видаляє,
заміна видаляє, курси валют ніколи не відкидаються, — описано в DATA.md (англійською)
у корені репозиторію. Повний приклад резервної копії з бюджетами, правилами, подорожами й боргами —
демонстраційний набір даних, на якому зроблено знімки екрана:
apps/mobile/screenshots/demo/kopiyka-demo.json.