Розробка мобільного додатку для управління складом (WMS)
Мобільна WMS — це не просто сканування штрихкодів. Це інтерфейс між складським робітником та системою управління запасами: приймання товару, розміщення на позиції, відбір для замовлення, переміщення між зонами, інвентаризація, відвантаження. Кожна операція вимагає підтвердження через сканування, а додаток працює в умовах слабкого Wi-Fi, гумових рукавиць та 10-годинних змін.
Обладнання: мобільні комп'ютери та термінали
Споживчий смартфон на складі — тимчасове рішення. Професійні мобільні комп'ютери (Zebra MC9300, Honeywell CT40, Point Mobile PM90) — це IP65/IP67 захист, змінні батареї, вбудований лазерний сканер з дальністю 10 метрів та підтримкою всіх одномірних та двомірних кодів.
Розробка для Zebra: DataWedge налаштовується через Intent API — додаток декларує профіль у datawedge.db або програмно через broadcast. Сканування приходить як Intent з extra com.symbol.datawedge.data_string:
class WarehouseActivity : AppCompatActivity() {
private val scanReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val barcode = intent.getStringExtra("com.symbol.datawedge.data_string") ?: return
val source = intent.getStringExtra("com.symbol.datawedge.label_type") // EAN13, CODE128, QR_CODE
viewModel.processBarcode(barcode, source)
}
}
override fun onResume() {
super.onResume()
registerReceiver(scanReceiver, IntentFilter("com.symbol.datawedge.api.RESULT_ACTION"))
// Включаємо DataWedge profile для нашого додатку
sendDataWedgeIntent("com.symbol.datawedge.api.SWITCH_TO_PROFILE", "WarehousePro")
}
}
Для Honeywell — Honeywell Scanning SDK (аналогічний Intent-механізм). Для універсальності: абстракція ScannerProvider з реалізаціями ZebraScanner, HoneywellScanner, CameraScanner. Визначаємо тип пристрою через Build.MANUFACTURER.
Завдання та черги: серверна модель
Складський робітник бачить список завдань, відсортованих за пріоритетом і маршрутом (щоб мінімізувати переміщення по складу). Завдання містить: тип операції, список товарних позицій з артикулами та кількістю, адреси ячейок (стеллаж-ярус-позиція).
@Entity(tableName = "tasks")
data class WarehouseTask(
@PrimaryKey val taskId: String,
val type: TaskType, // RECEIVING, PUTAWAY, PICKING, REPLENISHMENT, INVENTORY
val status: TaskStatus, // ASSIGNED, IN_PROGRESS, COMPLETED, BLOCKED
val priority: Int,
val assignedUserId: String,
val lines: List<TaskLine>,
val syncedAt: Long,
)
@Entity(tableName = "task_lines")
data class TaskLine(
@PrimaryKey val lineId: String,
val taskId: String,
val sku: String,
val description: String,
val expectedQty: Double,
val actualQty: Double?,
val fromLocation: String?,
val toLocation: String,
val lotNumber: String?,
val expiryDate: String?,
)
Офлайн-режим: при втраті Wi-Fi завдання доступні з Room. Всі операції записуються локально з timestamp, синхронізуються пакетом при відновленні з'єднання. Конфлікт версій (інший користувач обробив ту ж позицію) — сервер розв'язує за timestamp, додаток сповіщає про змінення.
Відбір за маршрутом (Picking)
Відбір — найбільш критична операція за часом. Сортування завдань за маршрутом обходу: S-образний маршрут (Serpentine) або найближчий сусід за адресами ячейок. Реалізується на бекенді, мобільний додаток отримує вже відсортований список.
Візуальний індикатор поточної ячейки: виділення на схемі складу або просто великий адрес (A-12-03) на весь екран. Підтвердження через сканування адреси ячейки — захист від помилок. Після сканування ячейки — сканування штрихкода товару, введення кількості (або автоматично 1 якщо одиничний відбір).
sealed class PickingStep {
object ScanLocation : PickingStep()
data class ScanItem(val expectedSku: String) : PickingStep()
data class EnterQuantity(val sku: String, val required: Double) : PickingStep()
data class Completed(val lineId: String) : PickingStep()
}
class PickingViewModel : ViewModel() {
private val _step = MutableStateFlow<PickingStep>(PickingStep.ScanLocation)
fun processBarcode(barcode: String) {
when (val step = _step.value) {
is PickingStep.ScanLocation -> {
if (barcode == currentLine.toLocation) {
_step.value = PickingStep.ScanItem(currentLine.sku)
} else {
_events.tryEmit(Event.WrongLocation(barcode, currentLine.toLocation))
}
}
is PickingStep.ScanItem -> {
if (barcode == step.expectedSku || isAlias(barcode, step.expectedSku)) {
_step.value = PickingStep.EnterQuantity(step.expectedSku, currentLine.expectedQty)
} else {
_events.tryEmit(Event.WrongItem(barcode))
}
}
else -> { /* обробка інших кроків */ }
}
}
}
Інтеграція з WMS/ERP
Типічні інтеграції: SAP WM/EWM через RFC/BAPI або REST (SAP API Hub), 1С:УПП/ERP через HTTP-сервіс, Microsoft Dynamics 365 через Dataverse API, кастомні WMS через REST/gRPC.
Стратегія інтеграції: не дергати ERP безпосередньо з мобільного — ставимо проміжний сервіс (middleware), який буферизує операції, обробляє помилки ERP та не роняє мобільний клієнт при 30-секундних відповідях SAP.
Розробка мобільної WMS-додатку з підтримкою мобільних комп'ютерів Zebra/Honeywell, офлайн-режимом, відбором за маршрутом та REST-інтеграцією: 3–5 місяців. Повний цикл (приймання, розміщення, відбір, інвентаризація, відвантаження) з інтеграцією SAP/1С: 5–8 місяців. Стоимість розраховується індивідуально.







