feat: implement BankAccountAdapter for mapping data layer values to domain entity and refactor BankAccountRepositoryImpl for improved staff ID handling
This commit is contained in:
@@ -84,3 +84,4 @@ export 'src/entities/availability/day_availability.dart';
|
||||
export 'src/adapters/profile/emergency_contact_adapter.dart';
|
||||
export 'src/adapters/profile/experience_adapter.dart';
|
||||
export 'src/entities/profile/experience_skill.dart';
|
||||
export 'src/adapters/profile/bank_account_adapter.dart';
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import '../../entities/profile/bank_account.dart';
|
||||
|
||||
/// Adapter for [BankAccount] to map data layer values to domain entity.
|
||||
class BankAccountAdapter {
|
||||
/// Maps primitive values to [BankAccount].
|
||||
static BankAccount fromPrimitives({
|
||||
required String id,
|
||||
required String userId,
|
||||
required String bankName,
|
||||
required String? type,
|
||||
String? accountNumber,
|
||||
String? last4,
|
||||
String? sortCode,
|
||||
bool? isPrimary,
|
||||
}) {
|
||||
return BankAccount(
|
||||
id: id,
|
||||
userId: userId,
|
||||
bankName: bankName,
|
||||
accountNumber: accountNumber ?? '',
|
||||
accountName: '', // Not provided by backend
|
||||
last4: last4,
|
||||
sortCode: sortCode,
|
||||
type: _stringToType(type),
|
||||
isPrimary: isPrimary ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
static BankAccountType _stringToType(String? value) {
|
||||
if (value == null) return BankAccountType.checking;
|
||||
try {
|
||||
// Assuming backend enum names match or are uppercase
|
||||
return BankAccountType.values.firstWhere(
|
||||
(e) => e.name.toLowerCase() == value.toLowerCase(),
|
||||
orElse: () => BankAccountType.other,
|
||||
);
|
||||
} catch (_) {
|
||||
return BankAccountType.other;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts domain type to string for backend.
|
||||
static String typeToString(BankAccountType type) {
|
||||
switch (type) {
|
||||
case BankAccountType.checking:
|
||||
return 'CHECKING';
|
||||
case BankAccountType.savings:
|
||||
return 'SAVINGS';
|
||||
default:
|
||||
return 'CHECKING';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,28 +2,24 @@ import 'package:firebase_auth/firebase_auth.dart' as auth;
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
import '../../domain/repositories/bank_account_repository.dart';
|
||||
|
||||
/// Implementation of [BankAccountRepository].
|
||||
/// Implementation of [BankAccountRepository] that integrates with Data Connect.
|
||||
class BankAccountRepositoryImpl implements BankAccountRepository {
|
||||
/// Creates a [BankAccountRepositoryImpl].
|
||||
const BankAccountRepositoryImpl({
|
||||
required this.dataConnect,
|
||||
required this.firebaseAuth,
|
||||
});
|
||||
|
||||
/// The Data Connect instance.
|
||||
final ExampleConnector dataConnect;
|
||||
/// The Firebase Auth instance.
|
||||
final auth.FirebaseAuth firebaseAuth;
|
||||
|
||||
@override
|
||||
Future<List<BankAccount>> getAccounts() async {
|
||||
final auth.User? user = firebaseAuth.currentUser;
|
||||
if (user == null) throw Exception('User not authenticated');
|
||||
final String? staffId = StaffSessionStore.instance.session?.staff?.id;
|
||||
if (staffId == null || staffId.isEmpty) {
|
||||
print('BankAccount getAccounts: missing staffId userId=${user.uid} session=${StaffSessionStore.instance.session}');
|
||||
throw Exception('Staff profile is missing.');
|
||||
}
|
||||
final String staffId = _getStaffId();
|
||||
|
||||
final QueryResult<GetAccountsByOwnerIdData, GetAccountsByOwnerIdVariables>
|
||||
result = await dataConnect
|
||||
@@ -31,29 +27,22 @@ class BankAccountRepositoryImpl implements BankAccountRepository {
|
||||
.execute();
|
||||
|
||||
return result.data.accounts.map((GetAccountsByOwnerIdAccounts account) {
|
||||
return BankAccount(
|
||||
return BankAccountAdapter.fromPrimitives(
|
||||
id: account.id,
|
||||
userId: account.ownerId,
|
||||
bankName: account.bank,
|
||||
accountNumber: account.accountNumber ?? '',
|
||||
accountNumber: account.accountNumber,
|
||||
last4: account.last4,
|
||||
accountName: '', // Not returned by API
|
||||
sortCode: account.routeNumber,
|
||||
type: _mapAccountType(account.type),
|
||||
isPrimary: account.isPrimary ?? false,
|
||||
type: account.type is Known<AccountType> ? (account.type as Known<AccountType>).value.name : null,
|
||||
isPrimary: account.isPrimary,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addAccount(BankAccount account) async {
|
||||
final auth.User? user = firebaseAuth.currentUser;
|
||||
if (user == null) throw Exception('User not authenticated');
|
||||
final String? staffId = StaffSessionStore.instance.session?.staff?.id;
|
||||
if (staffId == null || staffId.isEmpty) {
|
||||
print('BankAccount addAccount: missing staffId userId=${user.uid} session=${StaffSessionStore.instance.session}');
|
||||
throw Exception('Staff profile is missing.');
|
||||
}
|
||||
final String staffId = _getStaffId();
|
||||
|
||||
final QueryResult<GetAccountsByOwnerIdData, GetAccountsByOwnerIdVariables>
|
||||
existingAccounts = await dataConnect
|
||||
@@ -64,44 +53,41 @@ class BankAccountRepositoryImpl implements BankAccountRepository {
|
||||
|
||||
await dataConnect.createAccount(
|
||||
bank: account.bankName,
|
||||
type: _mapDomainType(account.type),
|
||||
type: AccountType.values.byName(BankAccountAdapter.typeToString(account.type)),
|
||||
last4: _safeLast4(account.last4, account.accountNumber),
|
||||
ownerId: staffId,
|
||||
).isPrimary(isPrimary).accountNumber(account.accountNumber).routeNumber(account.sortCode).execute();
|
||||
)
|
||||
.isPrimary(isPrimary)
|
||||
.accountNumber(account.accountNumber)
|
||||
.routeNumber(account.sortCode)
|
||||
.execute();
|
||||
}
|
||||
|
||||
BankAccountType _mapAccountType(EnumValue<AccountType> type) {
|
||||
if (type is Known<AccountType>) {
|
||||
switch (type.value) {
|
||||
case AccountType.CHECKING:
|
||||
return BankAccountType.checking;
|
||||
case AccountType.SAVINGS:
|
||||
return BankAccountType.savings;
|
||||
}
|
||||
}
|
||||
return BankAccountType.other;
|
||||
/// Helper to get the logged-in staff ID.
|
||||
String _getStaffId() {
|
||||
final auth.User? user = firebaseAuth.currentUser;
|
||||
if (user == null) {
|
||||
throw Exception('User not authenticated');
|
||||
}
|
||||
|
||||
AccountType _mapDomainType(BankAccountType type) {
|
||||
switch (type) {
|
||||
case BankAccountType.checking:
|
||||
return AccountType.CHECKING;
|
||||
case BankAccountType.savings:
|
||||
return AccountType.SAVINGS;
|
||||
default:
|
||||
return AccountType.CHECKING; // Default fallback
|
||||
final String? staffId = StaffSessionStore.instance.session?.staff?.id;
|
||||
if (staffId == null || staffId.isEmpty) {
|
||||
throw Exception('Staff profile is missing or session not initialized.');
|
||||
}
|
||||
return staffId;
|
||||
}
|
||||
|
||||
/// Ensures we have a last4 value, either from input or derived from account number.
|
||||
String _safeLast4(String? last4, String accountNumber) {
|
||||
if (last4 != null && last4.isNotEmpty) {
|
||||
return last4;
|
||||
}
|
||||
if (accountNumber.isEmpty) {
|
||||
return '';
|
||||
return '0000';
|
||||
}
|
||||
return accountNumber.length > 4
|
||||
? accountNumber.substring(accountNumber.length - 4)
|
||||
: accountNumber;
|
||||
if (accountNumber.length < 4) {
|
||||
return accountNumber.padLeft(4, '0');
|
||||
}
|
||||
return accountNumber.substring(accountNumber.length - 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../domain/arguments/add_bank_account_params.dart';
|
||||
import '../../domain/usecases/add_bank_account_usecase.dart';
|
||||
@@ -20,7 +19,7 @@ class BankAccountCubit extends Cubit<BankAccountState> {
|
||||
Future<void> loadAccounts() async {
|
||||
emit(state.copyWith(status: BankAccountStatus.loading));
|
||||
try {
|
||||
final accounts = await _getBankAccountsUseCase();
|
||||
final List<BankAccount> accounts = await _getBankAccountsUseCase();
|
||||
emit(state.copyWith(
|
||||
status: BankAccountStatus.loaded,
|
||||
accounts: accounts,
|
||||
@@ -45,7 +44,7 @@ class BankAccountCubit extends Cubit<BankAccountState> {
|
||||
emit(state.copyWith(status: BankAccountStatus.loading));
|
||||
|
||||
// Create domain entity
|
||||
final newAccount = BankAccount(
|
||||
final BankAccount newAccount = BankAccount(
|
||||
id: '', // Generated by server usually
|
||||
userId: '', // Handled by Repo/Auth
|
||||
bankName: 'New Bank', // Mock
|
||||
|
||||
@@ -40,7 +40,7 @@ class StaffProfileExperienceModule extends Module {
|
||||
);
|
||||
|
||||
// BLoC
|
||||
i.addLazySingleton<ExperienceBloc>(
|
||||
i.add<ExperienceBloc>(
|
||||
() => ExperienceBloc(
|
||||
getIndustries: i.get<GetStaffIndustriesUseCase>(),
|
||||
getSkills: i.get<GetStaffSkillsUseCase>(),
|
||||
|
||||
Reference in New Issue
Block a user