Розробка мобільного CRM-приложення
CRM-програма — це не CRUD-обгортка над базою даних. Реальна система управління клієнтами на мобільному пристрої вимагає офлайн-синхронізації, складної рольової моделі, push-повідомлень про подію в воронці продажу, інтеграцій з телефонією та поштою — і все це повинно працювати швидко навіть на застарілому Android-пристрої в зоні з нестабільним 3G.
Ключові технічні завдання
Офлайн-перший підхід та синхронізація
Менеджер з продажу в полі не може чекати завантаження даних клієнта, поки шукає Wi-Fi. CRM-програма обов'язково повинна працювати офлайн за замовчуванням. Стандартний підхід — локальна база SQLite з синхронізацією через розв'язання конфліктів на сервері.
На Flutter використовуємо drift (колишній moor) як типізований ORM поверх SQLite та connectivity_plus для відстеження стану мережі:
// Зберігаємо дію локально та ставимо в чергу синхронізації
Future<void> updateDealStage(String dealId, DealStage stage) async {
await localDb.updateDeal(dealId, stage: stage, syncStatus: SyncStatus.pending);
await syncQueue.enqueue(
SyncOperation(
type: OperationType.updateDeal,
payload: {'id': dealId, 'stage': stage.name},
createdAt: DateTime.now(),
),
);
// Запускаємо синхронізацію, якщо є мережа
if (await connectivity.checkConnectivity() != ConnectivityResult.none) {
syncService.flush();
}
}
Конфлікти виникають, коли один контакт редагується з різних пристроїв. Стратегія «остання запис перемагає» на рівні запису руйнує дані. Правильно: Last-Write-Wins на рівні поля, а не запису — з updated_at на кожне змінюване поле та вектор версій для критичних даних.
Рольова модель та права доступу
CRM без ролей не існує: менеджер видить тільки своїх клієнтів, керівник — весь відділ, адміністратор — все. Рольова модель повинна бути реалізована й на сервері (Row Level Security в PostgreSQL або middleware), й на клієнті — не для безпеки, а для UX: приховувати недоступні дії.
На iOS використовуємо Keychain для зберігання токена з прив'язкою до kSecAttrAccessibleWhenUnlockedThisDeviceOnly — токен не переходить на інший пристрій при відновленні з резервної копії, що критично для корпоративного CRM.
Інтеграція з телефонією
Функція дзвінка прямо з картки контакту — стандарт для мобільного CRM. На iOS використовуємо CallKit для нативної інтеграції: дзвінок через VoIP-провайдера (Twilio, Vonage) відображається як звичайний вхідний дзвінок з іменем із CRM, записується в історію дзвінків пристрою.
// iOS CallKit провайдер
class CRMCallProvider: NSObject, CXProviderDelegate {
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
// Підключаємо Twilio Voice SDK
TwilioVoice.connect(with: connectOptions) { [weak self] call, error in
guard let call = call else { return }
self?.activeCall = call
action.fulfill()
// Логуємо початок дзвінка в CRM
self?.crmService.logCallStarted(contactId: action.callUUID.uuidString)
}
}
}
На Android аналог — ConnectionService через TelecomManager.
Архітектура та стек
Для кросплатформного CRM-приложення Flutter — оптимальний вибір: одна кодова база покриває iOS та Android, що важливо при постійно мінливих вимогах бізнесу. Архітектура: BLoC + Clean Architecture, шар репозиторіїв ізолює локальну базу та API.
| Шар | Технології |
|---|---|
| UI | Flutter + Material 3 / Cupertino-адаптації |
| Управління станом | flutter_bloc (BLoC pattern) |
| Локальна БД | drift (SQLite), Hive для кеша |
| Мережа | Dio + Retrofit-генерація, Interceptor для оновлення токена |
| Синхронізація | WorkManager (Android) / BGTaskScheduler (iOS) |
| Push | Firebase Cloud Messaging + фоновий fetch |
| Аналітика | Firebase Analytics, Crashlytics |
Для нативної розробки iOS або Android — SwiftUI + Combine / Jetpack Compose + ViewModel відповідно.
Робота з push-повідомленнями
CRM-події — запланована зустріч, новий лід, прострочена задача — вимагають доставки push у фоні. На iOS UNUserNotificationCenter з content-available: 1 запускає приложення у фоні для оновлення даних. Критичний момент: iOS дає фоновому процесу не більше 30 секунд, і занадто частий фоновий fetch призводить до throttling системою.
Стратегія: push — тільки для повідомлення про подію, важкі дані завантажуються лінивобільки при відкритті нотифікації.
Етапи роботи
Аудит вимог та проектування data model → дизайн (якщо немає готового) → розробка API та мобільного клієнта паралельно → інтеграційне тестування синхронізації з edge cases → тестування на реальних пристроях з симуляцією втрати мережі → публікація в App Store / Google Play → підтримка.
Обов'язковий етап — нагрузкове тестування синхронізації при великій базі (10 000+ контактів). На слабих Android-пристроях SQLite-операції з великим dataset без правильної пагінації та індексів викликають ANR.
Строки
MVP з базовим функціоналом (контакти, угоди, задачі, офлайн): 8–12 тижнів. Повноцінне приложення з телефонією, інтеграціями (пошта, календар), розширеною аналітикою: 4–6 місяців. Вартість розраховується індивідуально після аналізу вимог.







