creation one order time
This commit is contained in:
@@ -10,6 +10,8 @@ class OneTimeOrder extends Equatable {
|
||||
required this.date,
|
||||
required this.location,
|
||||
required this.positions,
|
||||
this.vendorId,
|
||||
this.roleRates = const <String, double>{},
|
||||
});
|
||||
/// 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<OneTimeOrderPosition> positions;
|
||||
|
||||
/// Selected vendor id for this order.
|
||||
final String? vendorId;
|
||||
|
||||
/// Role hourly rates keyed by role id.
|
||||
final Map<String, double> roleRates;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[date, location, positions];
|
||||
List<Object?> get props => <Object?>[
|
||||
date,
|
||||
location,
|
||||
positions,
|
||||
vendorId,
|
||||
roleRates,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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<void> createOneTimeOrder(domain.OneTimeOrder order) {
|
||||
return Future.value();
|
||||
Future<void> 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<int>(
|
||||
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(<String>[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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,11 +145,15 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
||||
) async {
|
||||
emit(state.copyWith(status: OneTimeOrderStatus.loading));
|
||||
try {
|
||||
final Map<String, double> roleRates = <String, double>{
|
||||
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));
|
||||
|
||||
Reference in New Issue
Block a user