import 'package:uuid/uuid.dart'; import '../models/order_models.dart'; import 'company_profile_service.dart'; import 'sales_order_repository.dart'; class SalesOrderLineInput { const SalesOrderLineInput({ required this.description, required this.quantity, required this.unitPrice, this.productId, this.taxRate, }); final String description; final int quantity; final int unitPrice; final String? productId; final double? taxRate; } class SalesOrderService { SalesOrderService({ SalesOrderRepository? repository, CompanyProfileService? companyProfileService, }) : _repository = repository ?? SalesOrderRepository(), _companyProfileService = companyProfileService ?? CompanyProfileService(); final SalesOrderRepository _repository; final CompanyProfileService _companyProfileService; final Uuid _uuid = const Uuid(); static const Map> _transitions = { SalesOrderStatus.draft: [SalesOrderStatus.confirmed, SalesOrderStatus.cancelled], SalesOrderStatus.confirmed: [SalesOrderStatus.picking, SalesOrderStatus.cancelled], SalesOrderStatus.picking: [SalesOrderStatus.shipped, SalesOrderStatus.cancelled], SalesOrderStatus.shipped: [SalesOrderStatus.closed], SalesOrderStatus.closed: [], SalesOrderStatus.cancelled: [], }; Future> fetchOrders({SalesOrderStatus? status}) { return _repository.fetchOrders(status: status); } Future createOrder({ required String customerId, required String customerName, List lines = const [], DateTime? requestedShipDate, String? notes, String? assignedTo, }) async { final profile = await _companyProfileService.loadProfile(); final now = DateTime.now(); final orderId = _uuid.v4(); final lineItems = _buildItems(orderId, lines); final order = SalesOrder( id: orderId, orderNumber: _generateOrderNumber(now), customerId: customerId, customerNameSnapshot: customerName, orderDate: now, requestedShipDate: requestedShipDate, status: SalesOrderStatus.draft, subtotal: 0, taxAmount: 0, totalAmount: 0, notes: notes, assignedTo: assignedTo, workflowStage: _workflowStage(SalesOrderStatus.draft), createdAt: now, updatedAt: now, items: lineItems, ).recalculateTotals(defaultTaxRate: profile.taxRate); await _repository.upsertOrder(order); return order; } Future updateOrder( SalesOrder order, { List? replacedLines, DateTime? requestedShipDate, String? notes, String? assignedTo, }) async { final profile = await _companyProfileService.loadProfile(); final now = DateTime.now(); final nextItems = replacedLines != null ? _buildItems(order.id, replacedLines) : order.items; final updated = order .copyWith( requestedShipDate: requestedShipDate ?? order.requestedShipDate, notes: notes ?? order.notes, assignedTo: assignedTo ?? order.assignedTo, updatedAt: now, items: nextItems, ) .recalculateTotals(defaultTaxRate: profile.taxRate); await _repository.upsertOrder(updated); return updated; } Future transitionStatus(String orderId, SalesOrderStatus nextStatus, {bool force = false}) async { final order = await _repository.findById(orderId); if (order == null) { throw StateError('order not found: $orderId'); } if (!force && !_canTransition(order.status, nextStatus)) { throw StateError('invalid transition ${order.status.name} -> ${nextStatus.name}'); } final now = DateTime.now(); final updated = order.copyWith( status: nextStatus, workflowStage: _workflowStage(nextStatus), updatedAt: now, ); await _repository.upsertOrder(updated); return updated; } Future advanceStatus(String orderId) async { final order = await _repository.findById(orderId); if (order == null) { throw StateError('order not found: $orderId'); } final candidates = _transitions[order.status]; if (candidates == null || candidates.isEmpty) { return order; } return transitionStatus(orderId, candidates.first); } bool _canTransition(SalesOrderStatus current, SalesOrderStatus next) { final allowed = _transitions[current]; return allowed?.contains(next) ?? false; } List nextStatuses(SalesOrderStatus current) { return List.unmodifiable(_transitions[current] ?? const []); } List _buildItems(String orderId, List lines) { return lines.asMap().entries.map((entry) { final index = entry.key; final line = entry.value; return SalesOrderItem( id: _uuid.v4(), orderId: orderId, productId: line.productId, description: line.description, quantity: line.quantity, unitPrice: line.unitPrice, taxRate: line.taxRate ?? 0, sortIndex: index, ); }).toList(); } String _generateOrderNumber(DateTime timestamp) { final datePart = '${timestamp.year}${timestamp.month.toString().padLeft(2, '0')}${timestamp.day.toString().padLeft(2, '0')}'; final timePart = '${timestamp.hour.toString().padLeft(2, '0')}${timestamp.minute.toString().padLeft(2, '0')}'; return 'SO$datePart-$timePart-${timestamp.millisecondsSinceEpoch % 1000}'.toUpperCase(); } String _workflowStage(SalesOrderStatus status) { switch (status) { case SalesOrderStatus.draft: return 'order'; case SalesOrderStatus.confirmed: return 'ready'; case SalesOrderStatus.picking: return 'picking'; case SalesOrderStatus.shipped: return 'shipping'; case SalesOrderStatus.closed: return 'closed'; case SalesOrderStatus.cancelled: return 'cancelled'; } } }