Merge pull request #427 from Oloodi/408-feature-implement-paidunpaid-breaks---client-app-frontend-development
DataConnectService is now partially enbaled in the client application
This commit is contained in:
@@ -27,6 +27,9 @@ class DataConnectService with DataErrorHandler {
|
||||
/// Cache for the current staff ID to avoid redundant lookups.
|
||||
String? _cachedStaffId;
|
||||
|
||||
/// Cache for the current business ID to avoid redundant lookups.
|
||||
String? _cachedBusinessId;
|
||||
|
||||
/// Gets the current staff ID from session store or persistent storage.
|
||||
Future<String> getStaffId() async {
|
||||
// 1. Check Session Store
|
||||
@@ -41,15 +44,14 @@ class DataConnectService with DataErrorHandler {
|
||||
// 3. Fetch from Data Connect using Firebase UID
|
||||
final firebase_auth.User? user = _auth.currentUser;
|
||||
if (user == null) {
|
||||
throw Exception('User is not authenticated');
|
||||
throw const NotAuthenticatedException(
|
||||
technicalMessage: 'User is not authenticated',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final fdc.QueryResult<
|
||||
dc.GetStaffByUserIdData,
|
||||
dc.GetStaffByUserIdVariables
|
||||
>
|
||||
response = await executeProtected(
|
||||
final fdc.QueryResult<dc.GetStaffByUserIdData, dc.GetStaffByUserIdVariables>
|
||||
response = await executeProtected(
|
||||
() => connector.getStaffByUserId(userId: user.uid).execute(),
|
||||
);
|
||||
|
||||
@@ -65,6 +67,30 @@ class DataConnectService with DataErrorHandler {
|
||||
return user.uid;
|
||||
}
|
||||
|
||||
/// Gets the current business ID from session store or persistent storage.
|
||||
Future<String> getBusinessId() async {
|
||||
// 1. Check Session Store
|
||||
final dc.ClientSession? session = dc.ClientSessionStore.instance.session;
|
||||
if (session?.business?.id != null) {
|
||||
return session!.business!.id;
|
||||
}
|
||||
|
||||
// 2. Check Cache
|
||||
if (_cachedBusinessId != null) return _cachedBusinessId!;
|
||||
|
||||
// 3. Check Auth Status
|
||||
final firebase_auth.User? user = _auth.currentUser;
|
||||
if (user == null) {
|
||||
throw const NotAuthenticatedException(
|
||||
technicalMessage: 'User is not authenticated',
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Fallback (should ideally not happen if DB is seeded and session is initialized)
|
||||
// Ideally we'd have a getBusinessByUserId query here.
|
||||
return user.uid;
|
||||
}
|
||||
|
||||
/// Converts a Data Connect timestamp/string/json to a [DateTime].
|
||||
DateTime? toDateTime(dynamic t) {
|
||||
if (t == null) return null;
|
||||
@@ -116,5 +142,6 @@ class DataConnectService with DataErrorHandler {
|
||||
/// Clears the internal cache (e.g., on logout).
|
||||
void clearCache() {
|
||||
_cachedStaffId = null;
|
||||
_cachedBusinessId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
library;
|
||||
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
@@ -28,12 +27,7 @@ class ClientAuthenticationModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repositories
|
||||
i.addLazySingleton<AuthRepositoryInterface>(
|
||||
() => AuthRepositoryImpl(
|
||||
firebaseAuth: firebase.FirebaseAuth.instance,
|
||||
dataConnect: ExampleConnector.instance,
|
||||
),
|
||||
);
|
||||
i.addLazySingleton<AuthRepositoryInterface>(AuthRepositoryImpl.new);
|
||||
|
||||
// UseCases
|
||||
i.addLazySingleton(
|
||||
|
||||
@@ -12,7 +12,6 @@ import 'package:krow_domain/krow_domain.dart'
|
||||
AccountExistsException,
|
||||
UserNotFoundException,
|
||||
UnauthorizedAppException,
|
||||
UnauthorizedAppException,
|
||||
PasswordMismatchException,
|
||||
NetworkException;
|
||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
@@ -23,18 +22,13 @@ import '../../domain/repositories/auth_repository_interface.dart';
|
||||
///
|
||||
/// This implementation integrates with Firebase Authentication for user
|
||||
/// identity management and Krow's Data Connect SDK for storing user profile data.
|
||||
class AuthRepositoryImpl
|
||||
with dc.DataErrorHandler
|
||||
implements AuthRepositoryInterface {
|
||||
|
||||
class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
/// Creates an [AuthRepositoryImpl] with the real dependencies.
|
||||
AuthRepositoryImpl({
|
||||
required firebase.FirebaseAuth firebaseAuth,
|
||||
required dc.ExampleConnector dataConnect,
|
||||
}) : _firebaseAuth = firebaseAuth,
|
||||
_dataConnect = dataConnect;
|
||||
final firebase.FirebaseAuth _firebaseAuth;
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
dc.DataConnectService? service,
|
||||
}) : _service = service ?? dc.DataConnectService.instance;
|
||||
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
@override
|
||||
Future<domain.User> signInWithEmail({
|
||||
@@ -42,7 +36,8 @@ class AuthRepositoryImpl
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
final firebase.UserCredential credential = await _firebaseAuth.signInWithEmailAndPassword(
|
||||
final firebase.UserCredential credential =
|
||||
await _service.auth.signInWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
@@ -59,7 +54,6 @@ class AuthRepositoryImpl
|
||||
fallbackEmail: firebaseUser.email ?? email,
|
||||
requireBusinessRole: true,
|
||||
);
|
||||
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'invalid-credential' || e.code == 'wrong-password') {
|
||||
throw InvalidCredentialsException(
|
||||
@@ -94,7 +88,8 @@ class AuthRepositoryImpl
|
||||
|
||||
try {
|
||||
// Step 1: Try to create Firebase Auth user
|
||||
final firebase.UserCredential credential = await _firebaseAuth.createUserWithEmailAndPassword(
|
||||
final firebase.UserCredential credential =
|
||||
await _service.auth.createUserWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
@@ -111,9 +106,9 @@ class AuthRepositoryImpl
|
||||
firebaseUser: firebaseUser,
|
||||
companyName: companyName,
|
||||
email: email,
|
||||
onBusinessCreated: (String businessId) => createdBusinessId = businessId,
|
||||
onBusinessCreated: (String businessId) =>
|
||||
createdBusinessId = businessId,
|
||||
);
|
||||
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'weak-password') {
|
||||
throw WeakPasswordException(
|
||||
@@ -137,11 +132,13 @@ class AuthRepositoryImpl
|
||||
}
|
||||
} on domain.AppException {
|
||||
// Rollback for our known exceptions
|
||||
await _rollbackSignUp(firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
await _rollbackSignUp(
|
||||
firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
rethrow;
|
||||
} catch (e) {
|
||||
// Rollback: Clean up any partially created resources
|
||||
await _rollbackSignUp(firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
await _rollbackSignUp(
|
||||
firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
throw SignUpFailedException(
|
||||
technicalMessage: 'Unexpected error: $e',
|
||||
);
|
||||
@@ -164,11 +161,13 @@ class AuthRepositoryImpl
|
||||
required String password,
|
||||
required String companyName,
|
||||
}) async {
|
||||
developer.log('Email exists in Firebase, attempting sign-in: $email', name: 'AuthRepository');
|
||||
developer.log('Email exists in Firebase, attempting sign-in: $email',
|
||||
name: 'AuthRepository');
|
||||
|
||||
try {
|
||||
// Try to sign in with the provided password
|
||||
final firebase.UserCredential credential = await _firebaseAuth.signInWithEmailAndPassword(
|
||||
final firebase.UserCredential credential =
|
||||
await _service.auth.signInWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
@@ -181,28 +180,32 @@ class AuthRepositoryImpl
|
||||
}
|
||||
|
||||
// Sign-in succeeded! Check if user already has a BUSINESS account in PostgreSQL
|
||||
final bool hasBusinessAccount = await _checkBusinessUserExists(firebaseUser.uid);
|
||||
final bool hasBusinessAccount =
|
||||
await _checkBusinessUserExists(firebaseUser.uid);
|
||||
|
||||
if (hasBusinessAccount) {
|
||||
// User already has a KROW Client account
|
||||
developer.log('User already has BUSINESS account: ${firebaseUser.uid}', name: 'AuthRepository');
|
||||
developer.log('User already has BUSINESS account: ${firebaseUser.uid}',
|
||||
name: 'AuthRepository');
|
||||
throw AccountExistsException(
|
||||
technicalMessage: 'User ${firebaseUser.uid} already has BUSINESS role',
|
||||
);
|
||||
}
|
||||
|
||||
// User exists in Firebase but not in KROW PostgreSQL - create the entities
|
||||
developer.log('Creating BUSINESS account for existing Firebase user: ${firebaseUser.uid}', name: 'AuthRepository');
|
||||
developer.log(
|
||||
'Creating BUSINESS account for existing Firebase user: ${firebaseUser.uid}',
|
||||
name: 'AuthRepository');
|
||||
return await _createBusinessAndUser(
|
||||
firebaseUser: firebaseUser,
|
||||
companyName: companyName,
|
||||
email: email,
|
||||
onBusinessCreated: (_) {}, // No rollback needed for existing Firebase user
|
||||
);
|
||||
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
// Sign-in failed - check why
|
||||
developer.log('Sign-in failed with code: ${e.code}', name: 'AuthRepository');
|
||||
developer.log('Sign-in failed with code: ${e.code}',
|
||||
name: 'AuthRepository');
|
||||
|
||||
if (e.code == 'wrong-password' || e.code == 'invalid-credential') {
|
||||
// Password doesn't match - check what providers are available
|
||||
@@ -226,9 +229,11 @@ class AuthRepositoryImpl
|
||||
// We can't distinguish between "wrong password" and "no password provider"
|
||||
// due to Firebase deprecating fetchSignInMethodsForEmail.
|
||||
// The PasswordMismatchException message covers both scenarios.
|
||||
developer.log('Password mismatch or different provider for: $email', name: 'AuthRepository');
|
||||
developer.log('Password mismatch or different provider for: $email',
|
||||
name: 'AuthRepository');
|
||||
throw PasswordMismatchException(
|
||||
technicalMessage: 'Email $email: password mismatch or different auth provider',
|
||||
technicalMessage:
|
||||
'Email $email: password mismatch or different auth provider',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -236,9 +241,11 @@ class AuthRepositoryImpl
|
||||
|
||||
Future<bool> _checkBusinessUserExists(String firebaseUserId) async {
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response =
|
||||
await executeProtected(() => _dataConnect.getUserById(id: firebaseUserId).execute());
|
||||
await _service.run(
|
||||
() => _service.connector.getUserById(id: firebaseUserId).execute());
|
||||
final dc.GetUserByIdUser? user = response.data.user;
|
||||
return user != null && (user.userRole == 'BUSINESS' || user.userRole == 'BOTH');
|
||||
return user != null &&
|
||||
(user.userRole == 'BUSINESS' || user.userRole == 'BOTH');
|
||||
}
|
||||
|
||||
/// Creates Business and User entities in PostgreSQL for a Firebase user.
|
||||
@@ -250,38 +257,44 @@ class AuthRepositoryImpl
|
||||
}) async {
|
||||
// Create Business entity in PostgreSQL
|
||||
|
||||
final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables> createBusinessResponse =
|
||||
await executeProtected(() => _dataConnect.createBusiness(
|
||||
businessName: companyName,
|
||||
userId: firebaseUser.uid,
|
||||
rateGroup: dc.BusinessRateGroup.STANDARD,
|
||||
status: dc.BusinessStatus.PENDING,
|
||||
).execute());
|
||||
final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables>
|
||||
createBusinessResponse = await _service.run(() => _service.connector
|
||||
.createBusiness(
|
||||
businessName: companyName,
|
||||
userId: firebaseUser.uid,
|
||||
rateGroup: dc.BusinessRateGroup.STANDARD,
|
||||
status: dc.BusinessStatus.PENDING,
|
||||
)
|
||||
.execute());
|
||||
|
||||
final dc.CreateBusinessBusinessInsert businessData = createBusinessResponse.data.business_insert;
|
||||
final dc.CreateBusinessBusinessInsert businessData =
|
||||
createBusinessResponse.data.business_insert;
|
||||
onBusinessCreated(businessData.id);
|
||||
|
||||
// Check if User entity already exists in PostgreSQL
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> userResult =
|
||||
await executeProtected(() => _dataConnect.getUserById(id: firebaseUser.uid).execute());
|
||||
await _service.run(() =>
|
||||
_service.connector.getUserById(id: firebaseUser.uid).execute());
|
||||
final dc.GetUserByIdUser? existingUser = userResult.data.user;
|
||||
|
||||
if (existingUser != null) {
|
||||
// User exists (likely in another app like STAFF). Update role to BOTH.
|
||||
await executeProtected(() => _dataConnect.updateUser(
|
||||
id: firebaseUser.uid,
|
||||
)
|
||||
.userRole('BOTH')
|
||||
.execute());
|
||||
await _service.run(() => _service.connector
|
||||
.updateUser(
|
||||
id: firebaseUser.uid,
|
||||
)
|
||||
.userRole('BOTH')
|
||||
.execute());
|
||||
} else {
|
||||
// Create new User entity in PostgreSQL
|
||||
await executeProtected(() => _dataConnect.createUser(
|
||||
id: firebaseUser.uid,
|
||||
role: dc.UserBaseRole.USER,
|
||||
)
|
||||
.email(email)
|
||||
.userRole('BUSINESS')
|
||||
.execute());
|
||||
await _service.run(() => _service.connector
|
||||
.createUser(
|
||||
id: firebaseUser.uid,
|
||||
role: dc.UserBaseRole.USER,
|
||||
)
|
||||
.email(email)
|
||||
.userRole('BUSINESS')
|
||||
.execute());
|
||||
}
|
||||
|
||||
return _getUserProfile(
|
||||
@@ -298,7 +311,7 @@ class AuthRepositoryImpl
|
||||
// Delete business first (if created)
|
||||
if (businessId != null) {
|
||||
try {
|
||||
await _dataConnect.deleteBusiness(id: businessId).execute();
|
||||
await _service.connector.deleteBusiness(id: businessId).execute();
|
||||
} catch (_) {
|
||||
// Log but don't throw - we're already in error recovery
|
||||
}
|
||||
@@ -316,8 +329,9 @@ class AuthRepositoryImpl
|
||||
@override
|
||||
Future<void> signOut() async {
|
||||
try {
|
||||
await _firebaseAuth.signOut();
|
||||
await _service.auth.signOut();
|
||||
dc.ClientSessionStore.instance.clear();
|
||||
_service.clearCache();
|
||||
} catch (e) {
|
||||
throw Exception('Error signing out: ${e.toString()}');
|
||||
}
|
||||
@@ -325,7 +339,8 @@ class AuthRepositoryImpl
|
||||
|
||||
@override
|
||||
Future<domain.User> signInWithSocial({required String provider}) {
|
||||
throw UnimplementedError('Social authentication with $provider is not yet implemented.');
|
||||
throw UnimplementedError(
|
||||
'Social authentication with $provider is not yet implemented.');
|
||||
}
|
||||
|
||||
Future<domain.User> _getUserProfile({
|
||||
@@ -334,18 +349,24 @@ class AuthRepositoryImpl
|
||||
bool requireBusinessRole = false,
|
||||
}) async {
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response =
|
||||
await executeProtected(() => _dataConnect.getUserById(id: firebaseUserId).execute());
|
||||
await _service.run(() =>
|
||||
_service.connector.getUserById(id: firebaseUserId).execute());
|
||||
final dc.GetUserByIdUser? user = response.data.user;
|
||||
if (user == null) {
|
||||
throw UserNotFoundException(
|
||||
technicalMessage: 'Firebase UID $firebaseUserId not found in users table',
|
||||
technicalMessage:
|
||||
'Firebase UID $firebaseUserId not found in users table',
|
||||
);
|
||||
}
|
||||
if (requireBusinessRole && user.userRole != 'BUSINESS' && user.userRole != 'BOTH') {
|
||||
await _firebaseAuth.signOut();
|
||||
if (requireBusinessRole &&
|
||||
user.userRole != 'BUSINESS' &&
|
||||
user.userRole != 'BOTH') {
|
||||
await _service.auth.signOut();
|
||||
dc.ClientSessionStore.instance.clear();
|
||||
_service.clearCache();
|
||||
throw UnauthorizedAppException(
|
||||
technicalMessage: 'User role is ${user.userRole}, expected BUSINESS or BOTH',
|
||||
technicalMessage:
|
||||
'User role is ${user.userRole}, expected BUSINESS or BOTH',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -362,13 +383,17 @@ class AuthRepositoryImpl
|
||||
role: user.role.stringValue,
|
||||
);
|
||||
|
||||
final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> businessResponse =
|
||||
await executeProtected(() => _dataConnect.getBusinessesByUserId(
|
||||
userId: firebaseUserId,
|
||||
).execute());
|
||||
final dc.GetBusinessesByUserIdBusinesses? business = businessResponse.data.businesses.isNotEmpty
|
||||
? businessResponse.data.businesses.first
|
||||
: null;
|
||||
final QueryResult<dc.GetBusinessesByUserIdData,
|
||||
dc.GetBusinessesByUserIdVariables> businessResponse =
|
||||
await _service.run(() => _service.connector
|
||||
.getBusinessesByUserId(
|
||||
userId: firebaseUserId,
|
||||
)
|
||||
.execute());
|
||||
final dc.GetBusinessesByUserIdBusinesses? business =
|
||||
businessResponse.data.businesses.isNotEmpty
|
||||
? businessResponse.data.businesses.first
|
||||
: null;
|
||||
|
||||
dc.ClientSessionStore.instance.setSession(
|
||||
dc.ClientSession(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
|
||||
import 'data/repositories_impl/billing_repository_impl.dart';
|
||||
import 'domain/repositories/billing_repository.dart';
|
||||
@@ -19,11 +18,7 @@ class BillingModule extends Module {
|
||||
|
||||
|
||||
// Repositories
|
||||
i.addSingleton<BillingRepository>(
|
||||
() => BillingRepositoryImpl(
|
||||
dataConnect: ExampleConnector.instance,
|
||||
),
|
||||
);
|
||||
i.addSingleton<BillingRepository>(BillingRepositoryImpl.new);
|
||||
|
||||
// Use Cases
|
||||
i.addSingleton(GetCurrentBillAmountUseCase.new);
|
||||
|
||||
@@ -6,88 +6,78 @@ import '../../domain/repositories/billing_repository.dart';
|
||||
|
||||
/// Implementation of [BillingRepository] in the Data layer.
|
||||
///
|
||||
/// This class is responsible for retrieving billing data from the [FinancialRepositoryMock]
|
||||
/// (which represents the Data Connect layer) and mapping it to Domain entities.
|
||||
///
|
||||
/// It strictly adheres to the Clean Architecture data layer responsibilities:
|
||||
/// - No business logic (except necessary data transformation/filtering).
|
||||
/// - Delegates to data sources.
|
||||
class BillingRepositoryImpl
|
||||
with data_connect.DataErrorHandler
|
||||
implements BillingRepository {
|
||||
/// This class is responsible for retrieving billing data from the
|
||||
/// Data Connect layer and mapping it to Domain entities.
|
||||
class BillingRepositoryImpl implements BillingRepository {
|
||||
/// Creates a [BillingRepositoryImpl].
|
||||
///
|
||||
/// Requires the [financialRepository] to fetch financial data.
|
||||
BillingRepositoryImpl({
|
||||
required data_connect.ExampleConnector dataConnect,
|
||||
}) : _dataConnect = dataConnect;
|
||||
data_connect.DataConnectService? service,
|
||||
}) : _service = service ?? data_connect.DataConnectService.instance;
|
||||
|
||||
final data_connect.ExampleConnector _dataConnect;
|
||||
final data_connect.DataConnectService _service;
|
||||
|
||||
/// Fetches the current bill amount by aggregating open invoices.
|
||||
@override
|
||||
@override
|
||||
Future<double> getCurrentBillAmount() async {
|
||||
final String? businessId =
|
||||
data_connect.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return 0.0;
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final fdc.QueryResult<data_connect.ListInvoicesByBusinessIdData, data_connect.ListInvoicesByBusinessIdVariables> result = await executeProtected(() => _dataConnect
|
||||
.listInvoicesByBusinessId(businessId: businessId)
|
||||
.execute());
|
||||
final fdc.QueryResult<data_connect.ListInvoicesByBusinessIdData,
|
||||
data_connect.ListInvoicesByBusinessIdVariables> result =
|
||||
await _service.connector
|
||||
.listInvoicesByBusinessId(businessId: businessId)
|
||||
.execute();
|
||||
|
||||
return result.data.invoices
|
||||
.map(_mapInvoice)
|
||||
.where((Invoice i) => i.status == InvoiceStatus.open)
|
||||
.fold<double>(
|
||||
0.0,
|
||||
(double sum, Invoice item) => sum + item.totalAmount,
|
||||
);
|
||||
return result.data.invoices
|
||||
.map(_mapInvoice)
|
||||
.where((Invoice i) => i.status == InvoiceStatus.open)
|
||||
.fold<double>(
|
||||
0.0,
|
||||
(double sum, Invoice item) => sum + item.totalAmount,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Fetches the history of paid invoices.
|
||||
@override
|
||||
Future<List<Invoice>> getInvoiceHistory() async {
|
||||
final String? businessId =
|
||||
data_connect.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return <Invoice>[];
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final fdc.QueryResult<data_connect.ListInvoicesByBusinessIdData, data_connect.ListInvoicesByBusinessIdVariables> result = await executeProtected(() => _dataConnect
|
||||
.listInvoicesByBusinessId(
|
||||
businessId: businessId,
|
||||
)
|
||||
.limit(10)
|
||||
.execute());
|
||||
final fdc.QueryResult<data_connect.ListInvoicesByBusinessIdData,
|
||||
data_connect.ListInvoicesByBusinessIdVariables> result =
|
||||
await _service.connector
|
||||
.listInvoicesByBusinessId(
|
||||
businessId: businessId,
|
||||
)
|
||||
.limit(10)
|
||||
.execute();
|
||||
|
||||
return result.data.invoices.map(_mapInvoice).toList();
|
||||
return result.data.invoices.map(_mapInvoice).toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// Fetches pending invoices (Open or Disputed).
|
||||
@override
|
||||
@override
|
||||
Future<List<Invoice>> getPendingInvoices() async {
|
||||
final String? businessId =
|
||||
data_connect.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return <Invoice>[];
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final fdc.QueryResult<data_connect.ListInvoicesByBusinessIdData, data_connect.ListInvoicesByBusinessIdVariables> result = await executeProtected(() => _dataConnect
|
||||
.listInvoicesByBusinessId(businessId: businessId)
|
||||
.execute());
|
||||
final fdc.QueryResult<data_connect.ListInvoicesByBusinessIdData,
|
||||
data_connect.ListInvoicesByBusinessIdVariables> result =
|
||||
await _service.connector
|
||||
.listInvoicesByBusinessId(businessId: businessId)
|
||||
.execute();
|
||||
|
||||
return result.data.invoices
|
||||
.map(_mapInvoice)
|
||||
.where(
|
||||
(Invoice i) =>
|
||||
i.status == InvoiceStatus.open ||
|
||||
i.status == InvoiceStatus.disputed,
|
||||
)
|
||||
.toList();
|
||||
return result.data.invoices
|
||||
.map(_mapInvoice)
|
||||
.where(
|
||||
(Invoice i) =>
|
||||
i.status == InvoiceStatus.open ||
|
||||
i.status == InvoiceStatus.disputed,
|
||||
)
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// Fetches the estimated savings amount.
|
||||
@@ -101,86 +91,81 @@ class BillingRepositoryImpl
|
||||
/// Fetches the breakdown of spending.
|
||||
@override
|
||||
Future<List<InvoiceItem>> getSpendingBreakdown(BillingPeriod period) async {
|
||||
final String? businessId =
|
||||
data_connect.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return <InvoiceItem>[];
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final DateTime start;
|
||||
final DateTime end;
|
||||
if (period == BillingPeriod.week) {
|
||||
final int daysFromMonday = now.weekday - DateTime.monday;
|
||||
final DateTime monday = DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
).subtract(Duration(days: daysFromMonday));
|
||||
start = DateTime(monday.year, monday.month, monday.day);
|
||||
end = DateTime(monday.year, monday.month, monday.day + 6, 23, 59, 59, 999);
|
||||
} else {
|
||||
start = DateTime(now.year, now.month, 1);
|
||||
end = DateTime(now.year, now.month + 1, 0, 23, 59, 59, 999);
|
||||
}
|
||||
|
||||
final fdc.QueryResult<data_connect.ListShiftRolesByBusinessAndDatesSummaryData, data_connect.ListShiftRolesByBusinessAndDatesSummaryVariables> result = await executeProtected(() => _dataConnect
|
||||
.listShiftRolesByBusinessAndDatesSummary(
|
||||
businessId: businessId,
|
||||
start: _toTimestamp(start),
|
||||
end: _toTimestamp(end),
|
||||
)
|
||||
.execute());
|
||||
|
||||
final List<data_connect.ListShiftRolesByBusinessAndDatesSummaryShiftRoles>
|
||||
shiftRoles = result.data.shiftRoles;
|
||||
if (shiftRoles.isEmpty) {
|
||||
return <InvoiceItem>[];
|
||||
}
|
||||
|
||||
final Map<String, _RoleSummary> summary = <String, _RoleSummary>{};
|
||||
for (final data_connect.ListShiftRolesByBusinessAndDatesSummaryShiftRoles role
|
||||
in shiftRoles) {
|
||||
final String roleId = role.roleId;
|
||||
final String roleName = role.role.name;
|
||||
final double hours = role.hours ?? 0.0;
|
||||
final double totalValue = role.totalValue ?? 0.0;
|
||||
final _RoleSummary? existing = summary[roleId];
|
||||
if (existing == null) {
|
||||
summary[roleId] = _RoleSummary(
|
||||
roleId: roleId,
|
||||
roleName: roleName,
|
||||
totalHours: hours,
|
||||
totalValue: totalValue,
|
||||
);
|
||||
final DateTime now = DateTime.now();
|
||||
final DateTime start;
|
||||
final DateTime end;
|
||||
if (period == BillingPeriod.week) {
|
||||
final int daysFromMonday = now.weekday - DateTime.monday;
|
||||
final DateTime monday = DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
).subtract(Duration(days: daysFromMonday));
|
||||
start = DateTime(monday.year, monday.month, monday.day);
|
||||
end = DateTime(
|
||||
monday.year, monday.month, monday.day + 6, 23, 59, 59, 999);
|
||||
} else {
|
||||
summary[roleId] = existing.copyWith(
|
||||
totalHours: existing.totalHours + hours,
|
||||
totalValue: existing.totalValue + totalValue,
|
||||
);
|
||||
start = DateTime(now.year, now.month, 1);
|
||||
end = DateTime(now.year, now.month + 1, 0, 23, 59, 59, 999);
|
||||
}
|
||||
}
|
||||
|
||||
return summary.values
|
||||
.map(
|
||||
(_RoleSummary item) => InvoiceItem(
|
||||
id: item.roleId,
|
||||
invoiceId: item.roleId,
|
||||
staffId: item.roleName,
|
||||
workHours: item.totalHours,
|
||||
rate: item.totalHours > 0 ? item.totalValue / item.totalHours : 0,
|
||||
amount: item.totalValue,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
final fdc.QueryResult<
|
||||
data_connect.ListShiftRolesByBusinessAndDatesSummaryData,
|
||||
data_connect.ListShiftRolesByBusinessAndDatesSummaryVariables>
|
||||
result = await _service.connector
|
||||
.listShiftRolesByBusinessAndDatesSummary(
|
||||
businessId: businessId,
|
||||
start: _service.toTimestamp(start),
|
||||
end: _service.toTimestamp(end),
|
||||
)
|
||||
.execute();
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||
final DateTime utc = dateTime.toUtc();
|
||||
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||
final int nanoseconds =
|
||||
(utc.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanoseconds, seconds);
|
||||
final List<data_connect.ListShiftRolesByBusinessAndDatesSummaryShiftRoles>
|
||||
shiftRoles = result.data.shiftRoles;
|
||||
if (shiftRoles.isEmpty) {
|
||||
return <InvoiceItem>[];
|
||||
}
|
||||
|
||||
final Map<String, _RoleSummary> summary = <String, _RoleSummary>{};
|
||||
for (final data_connect
|
||||
.ListShiftRolesByBusinessAndDatesSummaryShiftRoles role
|
||||
in shiftRoles) {
|
||||
final String roleId = role.roleId;
|
||||
final String roleName = role.role.name;
|
||||
final double hours = role.hours ?? 0.0;
|
||||
final double totalValue = role.totalValue ?? 0.0;
|
||||
final _RoleSummary? existing = summary[roleId];
|
||||
if (existing == null) {
|
||||
summary[roleId] = _RoleSummary(
|
||||
roleId: roleId,
|
||||
roleName: roleName,
|
||||
totalHours: hours,
|
||||
totalValue: totalValue,
|
||||
);
|
||||
} else {
|
||||
summary[roleId] = existing.copyWith(
|
||||
totalHours: existing.totalHours + hours,
|
||||
totalValue: existing.totalValue + totalValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return summary.values
|
||||
.map(
|
||||
(_RoleSummary item) => InvoiceItem(
|
||||
id: item.roleId,
|
||||
invoiceId: item.roleId,
|
||||
staffId: item.roleName,
|
||||
workHours: item.totalHours,
|
||||
rate: item.totalHours > 0 ? item.totalValue / item.totalHours : 0,
|
||||
amount: item.totalValue,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
Invoice _mapInvoice(data_connect.ListInvoicesByBusinessIdInvoices invoice) {
|
||||
@@ -193,7 +178,7 @@ class BillingRepositoryImpl
|
||||
workAmount: invoice.amount,
|
||||
addonsAmount: invoice.otherCharges ?? 0,
|
||||
invoiceNumber: invoice.invoiceNumber,
|
||||
issueDate: invoice.issueDate.toDateTime(),
|
||||
issueDate: _service.toDateTime(invoice.issueDate)!,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'data/repositories_impl/client_create_order_repository_impl.dart';
|
||||
import 'domain/repositories/client_create_order_repository_interface.dart';
|
||||
import 'domain/usecases/create_one_time_order_usecase.dart';
|
||||
@@ -29,12 +28,7 @@ class ClientCreateOrderModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repositories
|
||||
i.addLazySingleton<ClientCreateOrderRepositoryInterface>(
|
||||
() => ClientCreateOrderRepositoryImpl(
|
||||
firebaseAuth: firebase.FirebaseAuth.instance,
|
||||
dataConnect: ExampleConnector.instance,
|
||||
),
|
||||
);
|
||||
i.addLazySingleton<ClientCreateOrderRepositoryInterface>(ClientCreateOrderRepositoryImpl.new);
|
||||
|
||||
// UseCases
|
||||
i.addLazySingleton(GetOrderTypesUseCase.new);
|
||||
@@ -44,12 +38,7 @@ class ClientCreateOrderModule extends Module {
|
||||
// BLoCs
|
||||
i.add<ClientCreateOrderBloc>(ClientCreateOrderBloc.new);
|
||||
i.add<RapidOrderBloc>(RapidOrderBloc.new);
|
||||
i.add<OneTimeOrderBloc>(
|
||||
() => OneTimeOrderBloc(
|
||||
i.get<CreateOneTimeOrderUseCase>(),
|
||||
ExampleConnector.instance,
|
||||
),
|
||||
);
|
||||
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
@@ -13,21 +12,16 @@ import '../../domain/repositories/client_create_order_repository_interface.dart'
|
||||
///
|
||||
/// It follows the KROW Clean Architecture by keeping the data layer focused
|
||||
/// on delegation and data mapping, without business logic.
|
||||
class ClientCreateOrderRepositoryImpl
|
||||
with dc.DataErrorHandler
|
||||
implements ClientCreateOrderRepositoryInterface {
|
||||
class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInterface {
|
||||
ClientCreateOrderRepositoryImpl({
|
||||
required firebase.FirebaseAuth firebaseAuth,
|
||||
required dc.ExampleConnector dataConnect,
|
||||
}) : _firebaseAuth = firebaseAuth,
|
||||
_dataConnect = dataConnect;
|
||||
required dc.DataConnectService service,
|
||||
}) : _service = service;
|
||||
|
||||
final firebase.FirebaseAuth _firebaseAuth;
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
@override
|
||||
Future<List<domain.OrderType>> getOrderTypes() {
|
||||
return Future.value(const <domain.OrderType>[
|
||||
return Future<List<domain.OrderType>>.value(const <domain.OrderType>[
|
||||
domain.OrderType(
|
||||
id: 'one-time',
|
||||
titleKey: 'client_create_order.types.one_time',
|
||||
@@ -55,100 +49,95 @@ class ClientCreateOrderRepositoryImpl
|
||||
|
||||
@override
|
||||
Future<void> createOneTimeOrder(domain.OneTimeOrder order) async {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('Business is missing. Please sign in again.');
|
||||
}
|
||||
final String? vendorId = order.vendorId;
|
||||
if (vendorId == null || vendorId.isEmpty) {
|
||||
throw Exception('Vendor is missing.');
|
||||
}
|
||||
final domain.OneTimeOrderHubDetails? hub = order.hub;
|
||||
if (hub == null || hub.id.isEmpty) {
|
||||
throw Exception('Hub is missing.');
|
||||
}
|
||||
return _service.run(() async {
|
||||
final String businessId = await _service.getBusinessId();
|
||||
final String? vendorId = order.vendorId;
|
||||
if (vendorId == null || vendorId.isEmpty) {
|
||||
throw Exception('Vendor is missing.');
|
||||
}
|
||||
final domain.OneTimeOrderHubDetails? hub = order.hub;
|
||||
if (hub == null || hub.id.isEmpty) {
|
||||
throw Exception('Hub is missing.');
|
||||
}
|
||||
|
||||
final DateTime orderDateOnly = DateTime(
|
||||
order.date.year,
|
||||
order.date.month,
|
||||
order.date.day,
|
||||
);
|
||||
final fdc.Timestamp orderTimestamp = _toTimestamp(orderDateOnly);
|
||||
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables>
|
||||
orderResult = await executeProtected(() => _dataConnect
|
||||
.createOrder(
|
||||
businessId: businessId,
|
||||
orderType: dc.OrderType.ONE_TIME,
|
||||
teamHubId: hub.id,
|
||||
final DateTime orderDateOnly = DateTime(
|
||||
order.date.year,
|
||||
order.date.month,
|
||||
order.date.day,
|
||||
);
|
||||
final fdc.Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly);
|
||||
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables> orderResult =
|
||||
await _service.connector
|
||||
.createOrder(
|
||||
businessId: businessId,
|
||||
orderType: dc.OrderType.ONE_TIME,
|
||||
teamHubId: hub.id,
|
||||
)
|
||||
.vendorId(vendorId)
|
||||
.eventName(order.eventName)
|
||||
.status(dc.OrderStatus.POSTED)
|
||||
.date(orderTimestamp)
|
||||
.execute();
|
||||
|
||||
final String orderId = orderResult.data.order_insert.id;
|
||||
|
||||
final int workersNeeded = order.positions.fold<int>(
|
||||
0,
|
||||
(int sum, domain.OneTimeOrderPosition position) => sum + position.count,
|
||||
);
|
||||
final String shiftTitle = 'Shift 1 ${_formatDate(order.date)}';
|
||||
final double shiftCost = _calculateShiftCost(order);
|
||||
|
||||
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables> shiftResult =
|
||||
await _service.connector
|
||||
.createShift(title: shiftTitle, orderId: orderId)
|
||||
.date(orderTimestamp)
|
||||
.location(hub.name)
|
||||
.locationAddress(hub.address)
|
||||
.latitude(hub.latitude)
|
||||
.longitude(hub.longitude)
|
||||
.placeId(hub.placeId)
|
||||
.city(hub.city)
|
||||
.state(hub.state)
|
||||
.street(hub.street)
|
||||
.country(hub.country)
|
||||
.status(dc.ShiftStatus.PENDING)
|
||||
.workersNeeded(workersNeeded)
|
||||
.filled(0)
|
||||
.durationDays(1)
|
||||
.cost(shiftCost)
|
||||
.execute();
|
||||
|
||||
final String shiftId = shiftResult.data.shift_insert.id;
|
||||
|
||||
for (final domain.OneTimeOrderPosition position in order.positions) {
|
||||
final DateTime start = _parseTime(order.date, position.startTime);
|
||||
final DateTime end = _parseTime(order.date, position.endTime);
|
||||
final DateTime normalizedEnd = end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
|
||||
final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
|
||||
final double rate = order.roleRates[position.role] ?? 0;
|
||||
final double totalValue = rate * hours * position.count;
|
||||
|
||||
await _service.connector
|
||||
.createShiftRole(
|
||||
shiftId: shiftId,
|
||||
roleId: position.role,
|
||||
count: position.count,
|
||||
)
|
||||
.vendorId(vendorId)
|
||||
.eventName(order.eventName)
|
||||
.status(dc.OrderStatus.POSTED)
|
||||
.date(orderTimestamp)
|
||||
.execute());
|
||||
.startTime(_service.toTimestamp(start))
|
||||
.endTime(_service.toTimestamp(normalizedEnd))
|
||||
.hours(hours)
|
||||
.breakType(_breakDurationFromValue(position.lunchBreak))
|
||||
.isBreakPaid(_isBreakPaid(position.lunchBreak))
|
||||
.totalValue(totalValue)
|
||||
.execute();
|
||||
}
|
||||
|
||||
final String orderId = orderResult.data.order_insert.id;
|
||||
|
||||
final int workersNeeded = order.positions.fold<int>(
|
||||
0,
|
||||
(int sum, domain.OneTimeOrderPosition position) => sum + position.count,
|
||||
);
|
||||
final String shiftTitle = 'Shift 1 ${_formatDate(order.date)}';
|
||||
final double shiftCost = _calculateShiftCost(order);
|
||||
|
||||
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables>
|
||||
shiftResult = await executeProtected(() => _dataConnect
|
||||
.createShift(title: shiftTitle, orderId: orderId)
|
||||
.date(orderTimestamp)
|
||||
.location(hub.name)
|
||||
.locationAddress(hub.address)
|
||||
.latitude(hub.latitude)
|
||||
.longitude(hub.longitude)
|
||||
.placeId(hub.placeId)
|
||||
.city(hub.city)
|
||||
.state(hub.state)
|
||||
.street(hub.street)
|
||||
.country(hub.country)
|
||||
.status(dc.ShiftStatus.PENDING)
|
||||
.workersNeeded(workersNeeded)
|
||||
.filled(0)
|
||||
.durationDays(1)
|
||||
.cost(shiftCost)
|
||||
.execute());
|
||||
|
||||
final String shiftId = shiftResult.data.shift_insert.id;
|
||||
|
||||
for (final domain.OneTimeOrderPosition position in order.positions) {
|
||||
final DateTime start = _parseTime(order.date, position.startTime);
|
||||
final DateTime end = _parseTime(order.date, position.endTime);
|
||||
final DateTime normalizedEnd =
|
||||
end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
|
||||
final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
|
||||
final double rate = order.roleRates[position.role] ?? 0;
|
||||
final double totalValue = rate * hours * position.count;
|
||||
|
||||
|
||||
|
||||
await executeProtected(() => _dataConnect
|
||||
.createShiftRole(
|
||||
shiftId: shiftId,
|
||||
roleId: position.role,
|
||||
count: position.count,
|
||||
)
|
||||
.startTime(_toTimestamp(start))
|
||||
.endTime(_toTimestamp(normalizedEnd))
|
||||
.hours(hours)
|
||||
.breakType(_breakDurationFromValue(position.lunchBreak))
|
||||
.isBreakPaid(_isBreakPaid(position.lunchBreak))
|
||||
.totalValue(totalValue)
|
||||
.execute());
|
||||
}
|
||||
|
||||
await executeProtected(() => _dataConnect
|
||||
.updateOrder(id: orderId, teamHubId: hub.id)
|
||||
.shifts(fdc.AnyValue(<String>[shiftId]))
|
||||
.execute());
|
||||
await _service.connector
|
||||
.updateOrder(id: orderId, teamHubId: hub.id)
|
||||
.shifts(fdc.AnyValue(<String>[shiftId]))
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -213,13 +202,6 @@ class ClientCreateOrderRepositoryImpl
|
||||
);
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||
final DateTime utc = dateTime.toUtc();
|
||||
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||
final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
||||
return fdc.Timestamp(nanoseconds, seconds);
|
||||
}
|
||||
|
||||
String _formatDate(DateTime dateTime) {
|
||||
final String year = dateTime.year.toString().padLeft(4, '0');
|
||||
final String month = dateTime.month.toString().padLeft(2, '0');
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'one_time_order_state.dart';
|
||||
/// BLoC for managing the multi-step one-time order creation form.
|
||||
class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
||||
with BlocErrorHandler<OneTimeOrderState>, SafeBloc<OneTimeOrderEvent, OneTimeOrderState> {
|
||||
OneTimeOrderBloc(this._createOneTimeOrderUseCase, this._dataConnect)
|
||||
OneTimeOrderBloc(this._createOneTimeOrderUseCase, this._service)
|
||||
: super(OneTimeOrderState.initial()) {
|
||||
on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded);
|
||||
on<OneTimeOrderVendorChanged>(_onVendorChanged);
|
||||
@@ -28,13 +28,13 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
||||
_loadHubs();
|
||||
}
|
||||
final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase;
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
final dc.DataConnectService _service;
|
||||
|
||||
Future<void> _loadVendors() async {
|
||||
final List<Vendor>? vendors = await handleErrorWithResult(
|
||||
action: () async {
|
||||
final QueryResult<dc.ListVendorsData, void> result =
|
||||
await _dataConnect.listVendors().execute();
|
||||
await _service.connector.listVendors().execute();
|
||||
return result.data.vendors
|
||||
.map(
|
||||
(dc.ListVendorsVendors vendor) => Vendor(
|
||||
@@ -57,7 +57,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
||||
final List<OneTimeOrderRoleOption>? roles = await handleErrorWithResult(
|
||||
action: () async {
|
||||
final QueryResult<dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdVariables>
|
||||
result = await _dataConnect.listRolesByVendorId(vendorId: vendorId).execute();
|
||||
result = await _service.connector.listRolesByVendorId(vendorId: vendorId).execute();
|
||||
return result.data.roles
|
||||
.map(
|
||||
(dc.ListRolesByVendorIdRoles role) => OneTimeOrderRoleOption(
|
||||
@@ -79,13 +79,9 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
||||
Future<void> _loadHubs() async {
|
||||
final List<OneTimeOrderHubOption>? hubs = await handleErrorWithResult(
|
||||
action: () async {
|
||||
final String? businessId =
|
||||
dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return <OneTimeOrderHubOption>[];
|
||||
}
|
||||
final String businessId = await _service.getBusinessId();
|
||||
final QueryResult<dc.ListTeamHubsByOwnerIdData, dc.ListTeamHubsByOwnerIdVariables>
|
||||
result = await _dataConnect
|
||||
result = await _service.connector
|
||||
.listTeamHubsByOwnerId(ownerId: businessId)
|
||||
.execute();
|
||||
return result.data.teamHubs
|
||||
|
||||
Reference in New Issue
Block a user