Если альфа скорринг пройден, не показываем кнопка привязки карты при консультации.

This commit is contained in:
2026-08-17 15:10:33 +03:00
parent 388607190f
commit ca1caa4adb
6 changed files with 112 additions and 10 deletions

View File

@@ -107,7 +107,9 @@ class CubitRegistrations {
sl.registerFactory<FeedProfileCubit>(() => FeedProfileCubit(profileRepository: sl()));
// Chat related cubits
sl.registerFactory<ChatRoomCubit>(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
sl.registerFactory<ChatRoomCubit>(
() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl(), consultantRepository: sl()),
);
// Profile related cubits
sl.registerFactory<ProfileCubit>(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));

View File

@@ -17,6 +17,7 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
import 'package:eatvio_app/features/chat/utils/chat_counterpart_utils.dart';
import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart';
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
@@ -27,6 +28,7 @@ import 'package:uuid/uuid.dart';
class ChatRoomCubit extends Cubit<ChatRoomState> {
final ChatRepository chatRepository;
final CardsRepository cardsRepository;
final ConsultantRepository consultantRepository;
final WsManager wsManager;
StreamSubscription<ChannelReadEvent>? typingSubscription;
@@ -39,7 +41,12 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
void Function(bool isOnline)? _presenceOnlineListener;
var uuid = const Uuid();
ChatRoomCubit({required this.chatRepository, required this.wsManager, required this.cardsRepository}) : super(const ChatRoomState.loading());
ChatRoomCubit({
required this.chatRepository,
required this.wsManager,
required this.cardsRepository,
required this.consultantRepository,
}) : super(const ChatRoomState.loading());
void setTimer() {
_timer?.cancel();
@@ -91,6 +98,18 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
}
}
Future<void> checkAlfaScoringStatus(String slug) async {
try {
final status = await consultantRepository.getMyRequestStatus(slug);
if (status.legalEntity?.alfaScoringStatus.isComplete == true) {
_emitLoaded((current) => current.copyWith(isAlfaScoringComplete: true));
}
} catch (e, st) {
await reportFrom(e, st, this);
log(e.toString(), stackTrace: st);
}
}
/// Инициализирует чат по [uuid] либо по [orderId] (resolve → conversation_uuid).
///
/// При наличии [orderId] сначала вызывается
@@ -137,8 +156,12 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
);
await _subscribeToPresenceChannel(chatId: chatRoom.id);
if (chosenPlan?.consultantId == chatRoom.users.sender.id) {
if (chosenPlan?.consultantId == wsManager.userId) {
final mySlug = chatRoom.users.sender.id == wsManager.userId
? chatRoom.users.sender.slug
: chatRoom.users.receiver.slug;
await checkCards();
await checkAlfaScoringStatus(mySlug);
}
return resolvedUuid;
} catch (e, st) {

View File

@@ -14,6 +14,7 @@ abstract class ChatRoomState with _$ChatRoomState {
required ChatRoomModel chatRoom,
required List<ChatMessageModel> chatMessages,
required bool hasCard,
@Default(false) bool isAlfaScoringComplete,
required bool isTyping,
required bool isLoading,
}) = ChatRoomStateLoaded;

View File

@@ -190,7 +190,10 @@ class _ChatScreenState extends State<ChatScreen> {
BannerType? bannerType;
if (chosenPlan == null || chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
bannerType = null;
} else if (amIConsultant && !state.hasCard && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
} else if (amIConsultant &&
!state.hasCard &&
!state.isAlfaScoringComplete &&
chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
bannerType = BannerType.card;
} else if (amIConsultant && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
bannerType = BannerType.date;

View File

@@ -36,6 +36,7 @@ void main() {
expect((state as ChatRoomStateLoaded).chatRoom, chatRoom);
expect(state.chatMessages, isEmpty);
expect(state.hasCard, isFalse);
expect(state.isAlfaScoringComplete, isFalse);
expect(state.isTyping, isFalse);
expect(state.isLoading, isFalse);
});

View File

@@ -9,6 +9,8 @@ import 'package:eatvio_app/features/chat/models/chat_user_model.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart';
import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart';
import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
import 'package:eatvio_app/features/chat/models/order_id_to_conversation_model.dart';
@@ -21,11 +23,13 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
import '../../../../fixtures/fixture_reader.dart';
import 'chat_room_cubit_test.mocks.dart';
@GenerateNiceMocks([
MockSpec<ChatRepository>(),
MockSpec<CardsRepository>(),
MockSpec<ConsultantRepository>(),
MockSpec<WsManager>(),
MockSpec<StreamSubscription<ChannelReadEvent>>(),
MockSpec<PusherChannelsClient>(),
@@ -36,8 +40,17 @@ void main() {
late ChatRoomCubit chatRoomCubit;
late MockChatRepository mockChatRepository;
late MockCardsRepository mockCardsRepository;
late MockConsultantRepository mockConsultantRepository;
late MockWsManager mockWsManager;
ConsultationRequestModel requestWithScoring(String scoringStatus) {
final json = FixtureReader.readAsMap('consultant/consultation_request.json');
final legal = Map<String, dynamic>.from(json['legal_entity_resource'] as Map);
legal['alfa_scoring_status'] = scoringStatus;
json['legal_entity_resource'] = legal;
return ConsultationRequestModel.fromJson(json);
}
const testChatRoom = chat_room.ChatRoomModel(
id: 'chat-id',
chatRoomSettings: ChatRoomSettingsModel(name: 'Test Room', state: 'active'),
@@ -67,8 +80,14 @@ void main() {
setUp(() {
mockChatRepository = MockChatRepository();
mockCardsRepository = MockCardsRepository();
mockConsultantRepository = MockConsultantRepository();
mockWsManager = MockWsManager();
chatRoomCubit = ChatRoomCubit(chatRepository: mockChatRepository, wsManager: mockWsManager, cardsRepository: mockCardsRepository);
chatRoomCubit = ChatRoomCubit(
chatRepository: mockChatRepository,
wsManager: mockWsManager,
cardsRepository: mockCardsRepository,
consultantRepository: mockConsultantRepository,
);
});
tearDown(() async {
@@ -405,7 +424,7 @@ void main() {
await chatRoomCubit.initChat('uuid');
await Future.delayed(const Duration(milliseconds: 200));
// Мокаем getCards без создания реальных CardModel
reset(mockCardsRepository);
when(mockCardsRepository.getCards()).thenAnswer((_) async => []);
await chatRoomCubit.checkCards();
@@ -1186,7 +1205,7 @@ void main() {
verifyNever(mockChatRepository.sendMessage(data: anyNamed('data'), files: anyNamed('files'), slug: anyNamed('slug')));
});
test('initChat - вызывает checkCards если consultantId == sender.id', () async {
test('initChat - вызывает checkCards и checkAlfaScoringStatus если consultantId == userId', () async {
const uuid = 'room-uuid';
const planId = 123;
final chatRoomWithPlan = testChatRoom.copyWith(chosenPlanId: planId);
@@ -1215,10 +1234,11 @@ void main() {
startDate: DateTime.now(),
chosenPlanState: ChosenPlanStateEnum.stateStarted,
chatId: 'chat-1',
consultantId: 1, // совпадает с sender.id
consultantId: 1,
plan: testPlan,
);
when(mockWsManager.userId).thenReturn(1);
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => chatRoomWithPlan);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => chosenPlan);
@@ -1228,14 +1248,16 @@ void main() {
when(mockWsManager.directChatStream).thenReturn(null);
when(mockWsManager.clientTypingStream).thenReturn(null);
when(mockCardsRepository.getCards()).thenAnswer((_) async => []);
when(mockConsultantRepository.getMyRequestStatus('user-1')).thenAnswer((_) async => requestWithScoring('none'));
await chatRoomCubit.initChat(uuid);
await Future.delayed(const Duration(milliseconds: 200));
verify(mockCardsRepository.getCards()).called(1);
verify(mockConsultantRepository.getMyRequestStatus('user-1')).called(1);
});
test('initChat - не вызывает checkCards если consultantId != sender.id', () async {
test('initChat - не вызывает checkCards если consultantId != userId', () async {
const uuid = 'room-uuid';
const planId = 123;
final chatRoomWithPlan = testChatRoom.copyWith(chosenPlanId: planId);
@@ -1264,10 +1286,11 @@ void main() {
startDate: DateTime.now(),
chosenPlanState: ChosenPlanStateEnum.stateStarted,
chatId: 'chat-1',
consultantId: 2, // не совпадает с sender.id (1)
consultantId: 2,
plan: testPlan,
);
when(mockWsManager.userId).thenReturn(1);
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => chatRoomWithPlan);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => chosenPlan);
@@ -1281,6 +1304,55 @@ void main() {
await Future.delayed(const Duration(milliseconds: 200));
verifyNever(mockCardsRepository.getCards());
verifyNever(mockConsultantRepository.getMyRequestStatus(any));
});
test('checkAlfaScoringStatus - устанавливает isAlfaScoringComplete при scoring_complete', () async {
const uuid = 'room-uuid';
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => testChatRoom);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockWsManager.client).thenReturn(null);
when(mockWsManager.initPrivateDirectChatChannel(any)).thenAnswer((_) async => Future.value());
when(mockWsManager.subscribeDirectChatChannel()).thenReturn(null);
when(mockWsManager.directChatStream).thenReturn(null);
when(mockWsManager.clientTypingStream).thenReturn(null);
when(mockChatRepository.getChosenPlan(id: anyNamed('id'))).thenAnswer((_) async => throw Exception('No plan'));
await chatRoomCubit.initChat(uuid);
await Future.delayed(const Duration(milliseconds: 200));
when(mockConsultantRepository.getMyRequestStatus('user-1'))
.thenAnswer((_) async => requestWithScoring('scoring_complete'));
await chatRoomCubit.checkAlfaScoringStatus('user-1');
await Future.delayed(const Duration(milliseconds: 100));
final state = chatRoomCubit.state as ChatRoomStateLoaded;
expect(state.isAlfaScoringComplete, true);
});
test('checkAlfaScoringStatus - не устанавливает isAlfaScoringComplete при scoring_failed', () async {
const uuid = 'room-uuid';
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => testChatRoom);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockWsManager.client).thenReturn(null);
when(mockWsManager.initPrivateDirectChatChannel(any)).thenAnswer((_) async => Future.value());
when(mockWsManager.subscribeDirectChatChannel()).thenReturn(null);
when(mockWsManager.directChatStream).thenReturn(null);
when(mockWsManager.clientTypingStream).thenReturn(null);
when(mockChatRepository.getChosenPlan(id: anyNamed('id'))).thenAnswer((_) async => throw Exception('No plan'));
await chatRoomCubit.initChat(uuid);
await Future.delayed(const Duration(milliseconds: 200));
when(mockConsultantRepository.getMyRequestStatus('user-1'))
.thenAnswer((_) async => requestWithScoring('scoring_failed'));
await chatRoomCubit.checkAlfaScoringStatus('user-1');
await Future.delayed(const Duration(milliseconds: 100));
final state = chatRoomCubit.state as ChatRoomStateLoaded;
expect(state.isAlfaScoringComplete, false);
});
test('choseStartPlanDate - обрабатывает ошибку', () async {