handler method
- @override
override
Must be implemented
Implementation
@override
Future<Response> handler() async {
final body = jsonDecode(await request.readAsString());
final event = HasuraEvent.fromJson(body['event']);
final createdAt = DateTime.parse(body['created_at']);
final order = event.data.current;
final orderOld = event.data.old;
print('TriggerStatusController: $order');
final umbrellaStatusEvent = StatusEvent(
timestamp: createdAt,
orderId: order!['umbrellaID'] ?? order['externalKey'],
statusName: order['statusName'],
whoDid: event.sessionVariables['x-hasura-user-email'] ?? 'system@dtk.api',
additionalInfo: {'comment': order['reasonRefusal'] ?? ''},
);
if (order['statusName'] == OrderStatusName.pickedAndSent.value) {
try {
print('INFO Rappi shipment');
final fullOrder = await orderRepository.getOrder(order['id']);
// * Раппи заказы имеют связь с storekeeperId склада.
final rappiWarehouse = await warehouseRepository
.getWarehouseByStorekeeperId(storeKeeperId: fullOrder.driverID!);
if (rappiWarehouse == null) {
print(
'ERROR: rappi warehouse with storeKeeperId '
'${fullOrder.driverID!} does not exist',
);
return Response.internalServerError(
body: 'ERROR: rappi warehouse with storeKeeperId '
'${fullOrder.driverID!} does not exist',
);
}
final shipment = ShipmentInsert(
orderId: fullOrder.id,
driverId: order['driverID'],
warehouseId: rappiWarehouse.id!,
departmentId: fullOrder.departmentId,
rows: [],
applied: false,
);
final resultShipmentId =
await documentRepository.createShipment(shipment);
await orderRepository.updateOrderShipmentId(
fullOrder.id!,
resultShipmentId,
);
var rows = <SheetRowInsert>[];
if (fullOrder.positions == null || fullOrder.positions!.isEmpty) {
print(
'ERROR Order ${fullOrder.shortCode} has no positions - '
'${fullOrder.positions}',
);
return Response.internalServerError(
body: 'ERROR Order ${fullOrder.shortCode} has no positions - '
'${fullOrder.positions}',
);
}
for (final position in fullOrder.positions!) {
rows.add(
SheetRowInsert(
productId: position.productID,
quantity: position.quantity,
shipmentId: resultShipmentId,
),
);
}
if (rows.isEmpty) {
print('INFO rows are empty ${fullOrder.shortCode} $rows');
}
await documentRepository.insertSheetRowsToShipment(rows: rows);
await documentRepository.updateShipmentAppliedFlag(
id: resultShipmentId,
);
print(
'INFO TriggerStatusController: insert ${rows.length} '
'products ${rows.first}. Order ${fullOrder.shortCode}. '
'Shipment $resultShipmentId',
);
return Response.ok(
'INFO TriggerStatusController: shipment was created successfully ($resultShipmentId)',
);
} catch (e) {
print(
'ERROR TriggerStatusController failure: '
'$e for creating shipment (orderId: ${order['id']}))',
);
return Response.internalServerError(
body: 'ERROR TriggerStatusController failure: '
'$e for creating shipment (orderId: ${order['id']}))',
);
}
}
if (orderOld != null &&
orderOld['statusName'] == OrderStatusName.pickedAndSent.value &&
order['statusName'] == OrderStatusName.returnedStock.value) {
return _handleReturnedStockRappiOrder(
order['id'],
order['shipmentId'],
order['driverID'],
);
}
if (order['statusName'] == OrderStatusName.driverAssigned.value &&
(orderOld == null ||
orderOld['statusName'] != OrderStatusName.recall.value)) {
final driverId = order['driverID'],
clientId = order['clientID'],
shortCode = order['shortCode'];
final client = await orderRepository.getClient(clientId);
//* Обновление расписания в CRM
final plannedDate = DateTime.tryParse(order['plannedDate'] ?? '');
// Так как расписания задаются максимум на 5 дней,
// то проверяем plannedDate не больше 5 дней относительно now()
if (client.districtId != null &&
plannedDate != null &&
plannedDate.isBefore(DateTime.now().add(Duration(days: 5)))) {
final failureOrSuccess = await orderRepository
.getDistrictLoadingAndSendSchedule(client.districtId!);
failureOrSuccess.fold(
(error) => print(
'failure: ${error.toString()} for getDistrictLoadingAndSendSchedule(${client.districtId!}) '),
(_) => print(
'Success for getDistrictLoadingAndSendSchedule(${client.districtId!}) '),
);
}
try {
final driver = await userRepository.getUser(driverId);
final pushCycleLog = [];
for (final firebaseMessageToken in driver.firebaseMessageTokens!) {
final failureOrMessage =
await pushNotificationRepository.getTokenAndSendMessage(
targetToken: firebaseMessageToken.token,
address: client.address!,
shortCode: shortCode,
);
failureOrMessage.fold(
(error) => pushCycleLog.add(error.message),
(response) => pushCycleLog.add(response.body),
);
}
if (pushCycleLog.isNotEmpty) {
print('TriggerStatusController: $pushCycleLog');
return Response.ok(
'Send push: message sent ${pushCycleLog.join(', ')}',
);
}
print('TriggerStatusController: firebaseMessageTokens is empty');
return Response.ok('Send push: firebaseMessageTokens is empty');
} catch (e) {
return Response.internalServerError(
body: 'Send push: failure on getting driver: $e');
}
}
var log = '';
if (orderOld != null &&
orderOld['statusName'] == OrderStatusName.recall.value &&
order['statusName'] == OrderStatusName.driverAssigned.value &&
order['driverID'].toString().toLowerCase().contains('rappi')) {
final orderDto = TriggerOrderCreateDto.fromJson(event.data.current!);
return _assignDriverRescuedRappiOrder(orderDto);
}
if (orderOld != null &&
orderOld['statusName'] == OrderStatusName.recall.value &&
order['statusName'] == OrderStatusName.driverConfirmed.value) {
try {
await orderRepository.saveOrder(order['id']);
} catch (e) {
print('SAVE ORDER FAILED: $e');
}
}
//* Для статуса Recall создаём Task в Umbrella
if (order['statusName'] == OrderStatusName.recall.value) {
try {
final department = await orderRepository.getDepartmentById(
order['departmentId'],
);
if (!department.enableTaskCreating!) {
return Response.ok(
'TriggerStatusController INFO: Task creation is disabled for department with id ${order['departmentId']}',
);
}
final refuseReason = order['driverRefuseReasonId'] != null
? OrderRefuseReason.getReasonByCode(
order['driverRefuseReasonId'],
)
: OrderRefuseReason.getReasonByOldField(
order['reasonRefusal'],
);
if (refuseReason.reason == OrderRefuseReason.badReason.reason) {
return Response.badRequest(
body:
'TriggerStatusController ERROR: reasonRefusal cannot be empty',
);
}
if (order['driverRefuseReasonId'] == null) {
await orderRepository.updateOrder(
Order(
id: order['id'],
driverRefuseReasonId: refuseReason.id,
),
);
if (refuseReason.reason == OrderRefuseReason.other.reason)
await orderRepository.insertOrderComment(
OrderComment(
orderId: order['id'],
authorId: order['driverID'] ?? 'admin',
comment: order['reasonRefusal'],
createdAt: DateTime.now().toUtc(),
source: 'dtk_api',
),
);
}
if (refuseReason.reason == OrderRefuseReason.badReason.reason) {
return Response.badRequest(
body: 'Incorrect driver refuse reason: '
'${order['driverRefuseReasonId']}',
);
}
final orderComments =
await orderRepository.getOrderComments(order['id']);
final rescueOrderComments = orderComments.map((orderComment) {
return RescueOrderComment.fromOrderComment(orderComment);
}).toList();
final task = Task(
orderId: order['umbrellaID'],
shortCode: order['shortCode'],
comments: rescueOrderComments,
driverCancelReason: refuseReason.reason,
whoDid: event.sessionVariables['x-hasura-role'] ?? 'admin',
);
await umbrellaRepository.createTask(task);
print(
'TriggerStatusController: task for order ${order['shortCode']} was created successfully',
);
} catch (e) {
return Response.internalServerError(
body: 'TriggerStatusController: Failed to create task for Umbrella '
'for order ${order['shortCode']}: $e',
);
}
}
//* Автопроброс статуса заказа из 'RescheduleOnDelivering' в 'Recall' или 'DriverConfirmed',
//* так как водитель сам хранит товар и ему его не нужно сдавать при модели "mobile warehouse"
if (order['statusName'] == OrderStatusName.rescheduleOnDelivering.value) {
final nextStatus = order['plannedDate'] == null
? OrderStatusName.recall.value
: OrderStatusName.driverConfirmed.value;
final updateStatusOrFailure = await orderRepository.updateOrderStatus(
order['id'],
nextStatus, //Вернуть когда дата будет опциональная OrderStatusName.recall.value,
);
updateStatusOrFailure.fold(
(error) => print('SKIP STATUSES FAILED: ' + error.message),
(response) => log = 'ResceduleOnDelivering changed to $nextStatus',
);
}
//* Автопроброс статуса заказа из 'ReturnOnDelivering' в OrderStatusName.recall.value
if (order['statusName'] == OrderStatusName.returnOnDelivering.value) {
final updateStatusOrFailure = await orderRepository.updateOrderStatus(
order['id'],
OrderStatusName.recall.value,
);
updateStatusOrFailure.fold(
(error) => print('SKIP STATUSES FAILED: ' + error.message),
(response) => log = 'ReturnOnDelivering changed to Recall',
);
}
//* Если status не нужен [CRM], то возвращаем [Response] - это прерывает код
if (umbrellaStatusEvent.status == null) {
var cause = 'Status not in '
'[RescheduleOnDelivering, ReturnOnDelivering, DriverOnTheWay, '
'Delivered, CanceledOnCall, CollectedMoney]';
print('Warning in TriggerStatusController: $cause. Log: $log');
return Response.ok('Warning: $cause. $log');
}
if (order['vendorDatalinkId'] == VendorDatalinkStatic.hilartesDefault) {
final failureOrResult =
await umbrellaRepository.sendStatusEvent(umbrellaStatusEvent);
return failureOrResult.fold(
(l) => Response.internalServerError(body: 'error: $l'),
(_) {
print('TriggerStatusController: $log');
return Response.ok('Send status to CRM completed. $log');
},
);
}
print('TriggerStatusController: $log');
return Response.ok('Status is not for Umbrella CRM. $log');
}