import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/storage/draft_storage.dart'; import 'expense_model.dart'; import 'expense_api.dart'; const expenseDraftKey = 'expense'; class ExpenseCreateState { final ExpenseModel expense; final bool isSubmitting; const ExpenseCreateState({required this.expense, this.isSubmitting = false}); ExpenseCreateState copyWith({ExpenseModel? expense, bool? isSubmitting}) { return ExpenseCreateState( expense: expense ?? this.expense, isSubmitting: isSubmitting ?? this.isSubmitting, ); } } class ExpenseCreateController extends StateNotifier { final ExpenseApi _api; ExpenseCreateController(this._api, {ExpenseModel? initial}) : super( ExpenseCreateState( expense: initial ?? ExpenseModel( id: '', expenseNo: '', createTime: DateTime.now(), updateTime: DateTime.now(), ), ), ); void updateCurrencyCode(String code, double excRto) { state = state.copyWith( expense: state.expense.copyWith(currencyCode: code, excRto: excRto), ); } void updateApprovedAmount(double amount) { state = state.copyWith( expense: state.expense.copyWith(approvedAmount: amount), ); } void updateDetailApprovedAmount(int index, double amount) { final details = [...state.expense.details]; details[index] = details[index].copyWith(approvedAmount: amount); state = state.copyWith(expense: state.expense.copyWith(details: details)); } void updateDetailCustomerVendor(int index, String name) { final details = [...state.expense.details]; details[index] = details[index].copyWith(customerVendorName: name); state = state.copyWith(expense: state.expense.copyWith(details: details)); } void updateDetailProjectId(int index, String id) { final details = [...state.expense.details]; details[index] = details[index].copyWith(projectId: id); state = state.copyWith(expense: state.expense.copyWith(details: details)); } void updateDetailAcctSubjectId(int index, String id) { final details = [...state.expense.details]; details[index] = details[index].copyWith(acctSubjectId: id); state = state.copyWith(expense: state.expense.copyWith(details: details)); } void updateDetailOffsetAmount(int index, double amount) { final details = [...state.expense.details]; details[index] = details[index].copyWith(offsetAmount: amount); state = state.copyWith(expense: state.expense.copyWith(details: details)); } void restoreFromDraft(ExpenseModel draft, ExpenseApi api) { state = ExpenseCreateState( expense: draft.copyWith( deptId: '', deptName: '', applicantId: '', applicantName: '', ), ); } void reset() { state = ExpenseCreateState( expense: ExpenseModel( id: '', expenseNo: '', createTime: DateTime.now(), updateTime: DateTime.now(), ), ); } void updatePurpose(String purpose) { state = state.copyWith(expense: state.expense.copyWith(purpose: purpose)); } ExpenseCreateState get currentState => state; void updateAttachments(List paths) { state = state.copyWith(expense: state.expense.copyWith(attachments: paths)); } void updatePaymentMethod(String method) { state = state.copyWith( expense: state.expense.copyWith(paymentMethod: method), ); } void updateRemark(String remark) { state = state.copyWith(expense: state.expense.copyWith(remark: remark)); } void setGenerateVoucher(bool value) { state = state.copyWith( expense: state.expense.copyWith(isGenerateVoucher: value), ); } void updateDept(String deptId, String deptName) { state = state.copyWith( expense: state.expense.copyWith(deptId: deptId, deptName: deptName), ); } void updateEmployee(String applicantId, String applicantName) { state = state.copyWith( expense: state.expense.copyWith( applicantId: applicantId, applicantName: applicantName, ), ); } void addDetail(ExpenseDetailModel detail) { final details = [...state.expense.details, detail]; state = state.copyWith(expense: state.expense.copyWith(details: details)); } void removeDetail(int index) { final details = [...state.expense.details]..removeAt(index); state = state.copyWith(expense: state.expense.copyWith(details: details)); } void removeDetailsByAeNo(String aeNo) { final details = state.expense.details.where((d) => d.aeNo != aeNo).toList(); state = state.copyWith(expense: state.expense.copyWith(details: details)); } void updateDetail(int index, ExpenseDetailModel detail) { final details = [...state.expense.details]; details[index] = detail; state = state.copyWith(expense: state.expense.copyWith(details: details)); } void recalculateAmount() { var totalAmount = 0.0; var approvedAmount = 0.0; for (final d in state.expense.details) { totalAmount += d.totalAmount; approvedAmount += d.approvedAmount; } state = state.copyWith( expense: state.expense.copyWith( totalAmount: totalAmount, approvedAmount: approvedAmount, ), ); } Future submit() async { state = state.copyWith(isSubmitting: true); try { final expense = state.expense.copyWith(status: 'pending'); // 手动拼 Map(提交流程专用,保持 camelCase key,与 BillSave 接口一致) final data = { 'id': expense.id, 'expenseNo': expense.expenseNo, 'expenseDate': expense.expenseDate?.toIso8601String(), 'applicantId': expense.applicantId, 'applicantName': expense.applicantName, 'deptId': expense.deptId, 'deptName': expense.deptName, 'currencyCode': expense.currencyCode, 'excRto': expense.excRto, 'isGenerateVoucher': expense.isGenerateVoucher, 'voucherNo': expense.voucherNo, 'approvedAmount': expense.approvedAmount, 'totalAmount': expense.totalAmount, 'totalAmtnSh': expense.totalAmtnSh, 'purpose': expense.purpose, 'remark': expense.remark, 'paymentMethod': expense.paymentMethod, 'status': expense.status, 'approvalInstanceId': expense.approvalInstanceId, 'previousInstanceIds': expense.previousInstanceIds, 'effectiveDate': expense.effectiveDate?.toIso8601String(), 'auditorId': expense.auditorId, 'version': expense.version, 'createTime': expense.createTime.toIso8601String(), 'updateTime': expense.updateTime.toIso8601String(), 'details': expense.details.map((d) => d.toJson()).toList(), 'attachments': expense.attachments, }; await _api.submit(data); await DraftStorage.delete(expenseDraftKey); return true; } catch (_) { return false; } finally { state = state.copyWith(isSubmitting: false); } } Future saveDraft() async { state = state.copyWith(isSubmitting: true); try { final data = state.expense.toJson(); data.remove('dept'); data.remove('deptName'); data.remove('usrNo'); data.remove('applicantName'); await DraftStorage.save(expenseDraftKey, data); return true; } catch (_) { return false; } finally { state = state.copyWith(isSubmitting: false); } } static Future loadDraft() async { final data = await DraftStorage.load(expenseDraftKey); if (data == null) return null; try { return ExpenseModel.fromJson(data); } catch (_) { return null; } } static Future hasDraft() => DraftStorage.has(expenseDraftKey); static Future deleteDraft() => DraftStorage.delete(expenseDraftKey); } final expenseCreateProvider = StateNotifierProvider.autoDispose .family(( ref, editId, ) { final api = ref.watch(expenseApiProvider); return ExpenseCreateController(api); });