From f0f812862593cefcad18e87a74c1941006569830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Salazar?= <73718835+joshrs23@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:03:43 -0500 Subject: [PATCH] creation one order time --- .../src/entities/orders/one_time_order.dart | 16 ++- .../client_create_order_repository_impl.dart | 128 +++++++++++++++++- .../blocs/one_time_order_bloc.dart | 6 +- 3 files changed, 146 insertions(+), 4 deletions(-) diff --git a/apps/mobile/packages/domain/lib/src/entities/orders/one_time_order.dart b/apps/mobile/packages/domain/lib/src/entities/orders/one_time_order.dart index 7fb15c9a..1e035a49 100644 --- a/apps/mobile/packages/domain/lib/src/entities/orders/one_time_order.dart +++ b/apps/mobile/packages/domain/lib/src/entities/orders/one_time_order.dart @@ -10,6 +10,8 @@ class OneTimeOrder extends Equatable { required this.date, required this.location, required this.positions, + this.vendorId, + this.roleRates = const {}, }); /// The specific date for the shift or event. final DateTime date; @@ -20,6 +22,18 @@ class OneTimeOrder extends Equatable { /// The list of positions and headcounts required for this order. final List positions; + /// Selected vendor id for this order. + final String? vendorId; + + /// Role hourly rates keyed by role id. + final Map roleRates; + @override - List get props => [date, location, positions]; + List get props => [ + date, + location, + positions, + vendorId, + roleRates, + ]; } diff --git a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart index 1aec25fb..7ac048e9 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart @@ -1,4 +1,6 @@ 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; import 'package:krow_domain/krow_domain.dart' as domain; import '../../domain/repositories/client_create_order_repository_interface.dart'; @@ -49,8 +51,81 @@ class ClientCreateOrderRepositoryImpl } @override - Future createOneTimeOrder(domain.OneTimeOrder order) { - return Future.value(); + Future createOneTimeOrder(domain.OneTimeOrder order) async { + final 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 vendorId = order.vendorId; + if (vendorId == null || vendorId.isEmpty) { + throw Exception('Vendor is missing.'); + } + + final orderTimestamp = _toTimestamp(order.date); + final orderResult = await _dataConnect + .createOrder(businessId: businessId, orderType: dc.OrderType.ONE_TIME) + .vendorId(vendorId) + .location(order.location) + .status(dc.OrderStatus.POSTED) + .date(orderTimestamp) + .execute(); + + final orderId = orderResult.data?.order_insert.id; + if (orderId == null) { + throw Exception('Order creation failed.'); + } + + final workersNeeded = order.positions.fold( + 0, + (sum, position) => sum + position.count, + ); + final shiftTitle = 'Shift 1 ${_formatDate(order.date)}'; + final shiftCost = _calculateShiftCost(order); + + final shiftResult = await _dataConnect + .createShift(title: shiftTitle, orderId: orderId) + .date(orderTimestamp) + .location(order.location) + .locationAddress(order.location) + .status(dc.ShiftStatus.PENDING) + .workersNeeded(workersNeeded) + .filled(0) + .durationDays(1) + .cost(shiftCost) + .execute(); + + final shiftId = shiftResult.data?.shift_insert.id; + if (shiftId == null) { + throw Exception('Shift creation failed.'); + } + + for (final position in order.positions) { + final start = _parseTime(order.date, position.startTime); + final end = _parseTime(order.date, position.endTime); + final normalizedEnd = + end.isBefore(start) ? end.add(const Duration(days: 1)) : end; + final hours = normalizedEnd.difference(start).inMinutes / 60.0; + final rate = order.roleRates[position.role] ?? 0; + final totalValue = rate * hours; + + await _dataConnect + .createShiftRole( + shiftId: shiftId, + roleId: position.role, + count: position.count, + ) + .startTime(_toTimestamp(start)) + .endTime(_toTimestamp(normalizedEnd)) + .hours(hours) + .totalValue(totalValue) + .execute(); + } + + await _dataConnect + .updateOrder(id: orderId) + .shifts(fdc.AnyValue([shiftId])) + .execute(); } @override @@ -58,4 +133,53 @@ class ClientCreateOrderRepositoryImpl // TO-DO: connect IA and return array with the information. throw UnimplementedError('Rapid order IA is not connected yet.'); } + + double _calculateShiftCost(domain.OneTimeOrder order) { + double total = 0; + for (final position in order.positions) { + final start = _parseTime(order.date, position.startTime); + final end = _parseTime(order.date, position.endTime); + final normalizedEnd = + end.isBefore(start) ? end.add(const Duration(days: 1)) : end; + final hours = normalizedEnd.difference(start).inMinutes / 60.0; + final rate = order.roleRates[position.role] ?? 0; + total += rate * hours; + } + return total; + } + + DateTime _parseTime(DateTime date, String time) { + if (time.trim().isEmpty) { + throw Exception('Shift time is missing.'); + } + + DateTime parsed; + try { + parsed = DateFormat.jm().parse(time); + } catch (_) { + parsed = DateFormat.Hm().parse(time); + } + + return DateTime( + date.year, + date.month, + date.day, + parsed.hour, + parsed.minute, + ); + } + + fdc.Timestamp _toTimestamp(DateTime dateTime) { + final utc = dateTime.toUtc(); + final seconds = utc.millisecondsSinceEpoch ~/ 1000; + final nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000; + return fdc.Timestamp(nanoseconds, seconds); + } + + String _formatDate(DateTime dateTime) { + final year = dateTime.year.toString().padLeft(4, '0'); + final month = dateTime.month.toString().padLeft(2, '0'); + final day = dateTime.day.toString().padLeft(2, '0'); + return '$year-$month-$day'; + } } diff --git a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart index ab4369f7..6690a272 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart @@ -145,11 +145,15 @@ class OneTimeOrderBloc extends Bloc { ) async { emit(state.copyWith(status: OneTimeOrderStatus.loading)); try { + final Map roleRates = { + for (final role in state.roles) role.id: role.costPerHour, + }; final OneTimeOrder order = OneTimeOrder( date: state.date, location: state.location, positions: state.positions, - // In a real app, we'd pass the vendorId here + vendorId: state.selectedVendor?.id, + roleRates: roleRates, ); await _createOneTimeOrderUseCase(OneTimeOrderArguments(order: order)); emit(state.copyWith(status: OneTimeOrderStatus.success));