handler method

  1. @override
Future<Response> handler()
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');
}