Usuń nieaktualny konflikt zakończenia kursu
This commit is contained in:
@@ -38,6 +38,37 @@ interface RouteActionDao {
|
|||||||
)
|
)
|
||||||
suspend fun unconfirmedFinishesAfter(routeId: String, afterEpochMillis: Long): List<RouteActionEntity>
|
suspend fun unconfirmedFinishesAfter(routeId: String, afterEpochMillis: Long): List<RouteActionEntity>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT EXISTS(
|
||||||
|
SELECT 1 FROM route_actions
|
||||||
|
WHERE routeId = :routeId
|
||||||
|
AND action = 'finish'
|
||||||
|
AND status = 'CONFIRMED'
|
||||||
|
AND createdAtEpochMillis > :afterEpochMillis
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
suspend fun hasConfirmedFinishAfter(routeId: String, afterEpochMillis: Long): Boolean
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE route_actions
|
||||||
|
SET status = 'CONFIRMED',
|
||||||
|
lastError = NULL,
|
||||||
|
updatedAtEpochMillis = :updatedAt
|
||||||
|
WHERE routeId = :routeId
|
||||||
|
AND action = 'finish'
|
||||||
|
AND createdAtEpochMillis < :beforeEpochMillis
|
||||||
|
AND status != 'CONFIRMED'
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
suspend fun confirmSupersededFinishes(
|
||||||
|
routeId: String,
|
||||||
|
beforeEpochMillis: Long,
|
||||||
|
updatedAt: Long = System.currentTimeMillis(),
|
||||||
|
)
|
||||||
|
|
||||||
@Query("SELECT * FROM route_actions WHERE status != 'CONFIRMED'")
|
@Query("SELECT * FROM route_actions WHERE status != 'CONFIRMED'")
|
||||||
suspend fun allUnsent(): List<RouteActionEntity>
|
suspend fun allUnsent(): List<RouteActionEntity>
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class RouteActionWorker(
|
|||||||
if (!NetworkMonitor(applicationContext).isCurrentlyValidated()) return Result.retry()
|
if (!NetworkMonitor(applicationContext).isCurrentlyValidated()) return Result.retry()
|
||||||
val clientActionId = inputData.getString(KEY_CLIENT_ACTION_ID) ?: return Result.failure()
|
val clientActionId = inputData.getString(KEY_CLIENT_ACTION_ID) ?: return Result.failure()
|
||||||
val action = dao.find(clientActionId) ?: return Result.failure()
|
val action = dao.find(clientActionId) ?: return Result.failure()
|
||||||
|
if (action.status == RouteActionStatus.Confirmed) return Result.success()
|
||||||
val startPrerequisite = if (action.action == RouteActionType.Finish) {
|
val startPrerequisite = if (action.action == RouteActionType.Finish) {
|
||||||
dao.latestStartBefore(action.routeId, action.createdAtEpochMillis)
|
dao.latestStartBefore(action.routeId, action.createdAtEpochMillis)
|
||||||
} else {
|
} else {
|
||||||
@@ -100,6 +101,9 @@ class RouteActionWorker(
|
|||||||
lastError = null,
|
lastError = null,
|
||||||
attemptIncrement = 0,
|
attemptIncrement = 0,
|
||||||
)
|
)
|
||||||
|
if (action.action == RouteActionType.Finish) {
|
||||||
|
dao.confirmSupersededFinishes(action.routeId, action.createdAtEpochMillis)
|
||||||
|
}
|
||||||
syncRepository.cacheConfirmedRoute(response.route, response.route.routeDate)
|
syncRepository.cacheConfirmedRoute(response.route, response.route.routeDate)
|
||||||
DriverSyncWorker.enqueue(applicationContext, response.route.routeDate, response.route.id)
|
DriverSyncWorker.enqueue(applicationContext, response.route.routeDate, response.route.id)
|
||||||
if (action.action == RouteActionType.Start) {
|
if (action.action == RouteActionType.Start) {
|
||||||
@@ -130,13 +134,23 @@ class RouteActionWorker(
|
|||||||
"server_route_status" to serverRoute?.driverLifecycleStatus(),
|
"server_route_status" to serverRoute?.driverLifecycleStatus(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if (serverRoute != null && routeActionMatchesServer(action, photoClientRequestIds, serverRoute)) {
|
val supersededByConfirmedFinish = action.action == RouteActionType.Finish &&
|
||||||
|
dao.hasConfirmedFinishAfter(action.routeId, action.createdAtEpochMillis)
|
||||||
|
if (serverRoute != null &&
|
||||||
|
(
|
||||||
|
routeActionMatchesServer(action, photoClientRequestIds, serverRoute) ||
|
||||||
|
routeActionWasSuperseded(action, supersededByConfirmedFinish, serverRoute)
|
||||||
|
)
|
||||||
|
) {
|
||||||
dao.updateStatus(
|
dao.updateStatus(
|
||||||
clientActionId = clientActionId,
|
clientActionId = clientActionId,
|
||||||
status = RouteActionStatus.Confirmed,
|
status = RouteActionStatus.Confirmed,
|
||||||
lastError = null,
|
lastError = null,
|
||||||
attemptIncrement = 0,
|
attemptIncrement = 0,
|
||||||
)
|
)
|
||||||
|
if (action.action == RouteActionType.Finish) {
|
||||||
|
dao.confirmSupersededFinishes(action.routeId, action.createdAtEpochMillis)
|
||||||
|
}
|
||||||
syncRepository.cacheConfirmedRoute(serverRoute, serverRoute.routeDate)
|
syncRepository.cacheConfirmedRoute(serverRoute, serverRoute.routeDate)
|
||||||
DriverSyncWorker.enqueue(applicationContext, serverRoute.routeDate, serverRoute.id)
|
DriverSyncWorker.enqueue(applicationContext, serverRoute.routeDate, serverRoute.id)
|
||||||
return@getOrElse Result.success()
|
return@getOrElse Result.success()
|
||||||
@@ -233,6 +247,15 @@ internal fun routeActionMatchesServer(
|
|||||||
return weightMatches && notesMatch && photosMatch
|
return weightMatches && notesMatch && photosMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun routeActionWasSuperseded(
|
||||||
|
action: RouteActionEntity,
|
||||||
|
hasConfirmedFinishAfter: Boolean,
|
||||||
|
serverRoute: DriverRouteDto,
|
||||||
|
): Boolean =
|
||||||
|
action.action == RouteActionType.Finish &&
|
||||||
|
hasConfirmedFinishAfter &&
|
||||||
|
serverRoute.driverLifecycleStatus() == "ZAKOŃCZONA"
|
||||||
|
|
||||||
internal fun routeActionConflictMessage(message: String, serverRoute: DriverRouteDto?): String {
|
internal fun routeActionConflictMessage(message: String, serverRoute: DriverRouteDto?): String {
|
||||||
val serverStatus = serverRoute?.driverLifecycleStatus() ?: return message
|
val serverStatus = serverRoute?.driverLifecycleStatus() ?: return message
|
||||||
val operationalStatus = serverRoute.operationalStatus
|
val operationalStatus = serverRoute.operationalStatus
|
||||||
|
|||||||
+30
@@ -50,6 +50,36 @@ class RouteActionServerReconciliationTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun laterConfirmedFinishSupersedesAnOlderConflictingFinish() {
|
||||||
|
assertTrue(
|
||||||
|
routeActionWasSuperseded(
|
||||||
|
action = finish(weight = 11.8, notes = "Starsza uwaga"),
|
||||||
|
hasConfirmedFinishAfter = true,
|
||||||
|
serverRoute = route(
|
||||||
|
status = "ZAKOŃCZONA",
|
||||||
|
weight = null,
|
||||||
|
notes = null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun completedServerRouteAloneDoesNotDiscardConflictingLocalData() {
|
||||||
|
assertFalse(
|
||||||
|
routeActionWasSuperseded(
|
||||||
|
action = finish(weight = 11.8, notes = "Nadal ważna uwaga"),
|
||||||
|
hasConfirmedFinishAfter = false,
|
||||||
|
serverRoute = route(
|
||||||
|
status = "ZAKOŃCZONA",
|
||||||
|
weight = null,
|
||||||
|
notes = null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun finish(weight: Double?, notes: String?): RouteActionEntity =
|
private fun finish(weight: Double?, notes: String?): RouteActionEntity =
|
||||||
RouteActionEntity(
|
RouteActionEntity(
|
||||||
clientActionId = "finish-1",
|
clientActionId = "finish-1",
|
||||||
|
|||||||
Reference in New Issue
Block a user