Уявіть: склад із 500+ одиницями техніки, менеджери витрачають години на перехресне бронювання, а клієнти скаржаться на дублі. Класичний Excel-облік дає 30% помилок в інвентаризації. Ми розробили мобільний додаток під ключ, який виключає ці проблеми: QR-інвентаризація, автоматичне бронювання в реальному часі, фотофіксація стану. Система побудована на зв'язці Swift 5.9+ для iOS, Kotlin Jetpack Compose для Android та Python FastAPI на сервері. Дані синхронізуються через GraphQL і Firebase Cloud Messaging. Управління орендою стає прозорим: кожна операція фіксується, клієнти бачать реальну доступність техніки. Завдяки автоматизації компанія економить до 2 млн на рік при парку від 500 одиниць. Накопичений досвід дозволив запустити 12 подібних рішень для складів від 100 до 2000 одиниць. Середній повернення інвестицій — 4 місяці за рахунок скорочення простоїв та усунення конфліктів. Оцінимо ваш проект за 2 дні. Зв'яжіться з нами, щоб дізнатися, як прискорити видачу в 5 разів.
Які проблеми вирішує додаток?
Втрата обладнання та помилки обліку
Без системи кожна одиниця потребує ручного введення. Наш досвід показує: впровадження QR-інвентаризації знижує втрати на 95%. За статистикою, 60% суперечок при поверненні вирішуються завдяки фотофіксації.
Подвійне бронювання та втрачені замовлення
Конфлікти бронювань — часта головна біль. Автоматична перевірка доступності з блокуванням дат у реальному часі виключає перетинання. Серверне оптимістичне блокування дає миттєву відповідь: 200 OK або 409 Conflict.
Суперечки при поверненні обладнання
Фотофіксація стану до та після оренди — залізний доказ комплектності. Додаток стискає знімки, зберігаючи EXIF, і прикріплює їх до акту.
Роль фотофіксації при видачі
Перед видачею оператор фотографує обладнання. Фото прикріплюються до договору як доказ стану. Це критично для вирішення суперечок при поверненні.
// Android: CameraX для фотофиксации состояния
private fun takeHandoverPhoto() {
val imageCapture = imageCapture ?: return
val outputOptions = ImageCapture.OutputFileOptions.Builder(
createTempFile("handover_${System.currentTimeMillis()}", ".jpg")
).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val photoFile = output.savedUri?.toFile() ?: return
viewModel.addHandoverPhoto(photoFile, currentRentalId)
}
override fun onError(exception: ImageCaptureException) {
showError("Ошибка съёмки: ${exception.message}")
}
}
)
}
Фотографії стискаються до 1200px по довгій стороні перед завантаженням на сервер — оригінальний 12MP знімок на 4 МБ для цього завдання зайвий.
Як працює QR-інвентаризація?
Кожна одиниця обладнання отримує унікальний ідентифікатор — QR-код або штрихкод, наклеєний на корпус. Оператор сканує його для видачі та приймання:
// iOS: быстрый скан через AVCaptureSession
class BarcodeScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
func setupCapture() {
let session = AVCaptureSession()
guard let device = AVCaptureDevice.default(for: .video),
let input = try? AVCaptureDeviceInput(device: device) else { return }
session.addInput(input)
let output = AVCaptureMetadataOutput()
session.addOutput(output)
output.setMetadataObjectsDelegate(self, queue: .main)
output.metadataObjectTypes = [.qr, .code128, .ean13]
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)
session.startRunning()
}
func metadataOutput(
_ output: AVCaptureMetadataOutput,
didOutput metadataObjects: [AVMetadataObject],
from connection: AVCaptureConnection
) {
guard let readableCode = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
let assetId = readableCode.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
loadEquipmentDetails(assetId: assetId)
}
}
Після скану додаток показує картку обладнання: назва, серійний номер, поточний статус (AVAILABLE / RENTED / MAINTENANCE), історію оренд.
Порівняння: ручне введення vs QR-сканування
| Критерій | Ручне введення | QR-сканування |
|---|---|---|
| Час на одиницю | 15–30 сек | 1–2 сек |
| Помилки введення | 5–10% | <0.1% |
| Навчання оператора | 1 година | 5 хвилин |
QR-сканування в 15 разів швидше ручного введення — це прискорює видачу в 5 разів.
Як захиститися від подвійного бронювання?
Клієнтський додаток показує календар доступності. Перетинання неприпустимі. Логіка перевірки — на сервері, але UI блокує зайняті дати:
// iOS: DatePicker с недоступными датами
func configureDatePicker(unavailableDates: [DateInterval]) {
datePicker.minimumDate = Date()
// Для более гибкого контроля используем кастомный calendar component
// с блокировкой конкретных диапазонов
calendarView.disabledDateRanges = unavailableDates.map {
CalendarDateRange(start: $0.start, end: $0.end)
}
}
При конкурентному бронюванні (два користувачі намагаються забронювати одночасно) — серверне оптимістичне блокування: перший отримує підтвердження, другий — помилку 409 Conflict з пропозицією обрати інші дати.
Як розраховується вартість оренди?
Тарифікація може бути погодинною, подобовою або комбінованою. Серверний розрахунок:
def calculate_rental_cost(
equipment: Equipment,
start_date: datetime,
end_date: datetime
) -> Decimal:
duration = end_date - start_date
hours = duration.total_seconds() / 3600
if hours <= 4:
return equipment.hourly_rate * Decimal(str(hours))
elif hours <= 24:
return equipment.daily_rate # посуточная ставка выгоднее
else:
days = math.ceil(hours / 24)
return equipment.daily_rate * Decimal(str(days))
Додаток показує розрахунок у реальному часі при зміні дат — через debounced API-запит або локальний розрахунок з тими ж правилами.
Депозит та застава
Для дорогого обладнання береться застава. Схема: при бронюванні блокується сума на картці (authorization hold), при поверненні обладнання в цілості — розблоковується (void), при пошкодженні — списується (capture).
// Stripe: authorization-only payment intent
let params = STPPaymentIntentParams(clientSecret: clientSecret)
params.captureMethod = .manual // захватим позже при необходимости
STPPaymentHandler.shared().confirmPayment(params, with: self) { status, intent, error in
// intent.id сохраняем — нужен для capture или void при возврате
}
Процес роботи
- Аналітика — вивчаємо бізнес-процеси, узгоджуємо прототип.
- Проектування — UX/UI дизайн, архітектура бази даних.
- Розробка — iOS (Swift 5.9+, SwiftUI), Android (Kotlin, Jetpack Compose), сервер (Python/FastAPI).
- Тестування — модульні тести, інтеграція з App Store Connect та Google Play Console.
- Деплой — публікація в магазини, налаштування push-сповіщень (APNs/FCM).
Типові терміни етапів
| Етап | Тривалість |
|---|---|
| Аналітика та прототип | 1–2 тижні |
| Дизайн (UX/UI) | 2–3 тижні |
| Розробка мобільного додатку (iOS + Android) | 4–6 тижнів |
| Бекенд та інтеграція | 2–3 тижні |
| Тестування та деплой | 1–2 тижні |
Що входить у роботу
- Вихідний код та документація API.
- Доступи до бекенду та адмін-панелі.
- Навчання операторів (2 години).
- Технічна підтримка 30 днів після запуску.
- Допомога в проходженні App Store Review Guidelines (Section 4.2/5.1).
Типові помилки при впровадженні: пропуск налаштування прав доступу для ролей (оператор vs адміністратор), відсутність тестування при слабкому сигналі — критично для QR-сканування, ігнорування вимог до зберігання персональних даних (GDPR/152-ФЗ). Уникнути їх допоможе наш досвід — ми закладаємо коректні конфігурації на етапі проектування.
Орієнтири за термінами
Базова версія (каталог, бронювання, QR-інвентаризація, оплата): 5–7 тижнів. Операторський модуль з фотофіксацією та актами — ще 2–3 тижні. Вартість розраховується індивідуально, але економія від впровадження сягає 2 млн на рік для середнього складу.
Ми — команда з 7+ роками досвіду в розробці мобільних рішень для оренди. Гарантуємо проходження App Store Review та Google Play. Отримайте консультацію щодо вашого проекту — ми підготуємо комерційну пропозицію за 2 дні.







