expense_create_controller.dart 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import 'package:flutter_riverpod/flutter_riverpod.dart';
  2. import '../../core/storage/draft_storage.dart';
  3. import 'expense_model.dart';
  4. import 'expense_api.dart';
  5. const expenseDraftKey = 'expense';
  6. class ExpenseCreateState {
  7. final ExpenseModel expense;
  8. final bool isSubmitting;
  9. const ExpenseCreateState({required this.expense, this.isSubmitting = false});
  10. ExpenseCreateState copyWith({ExpenseModel? expense, bool? isSubmitting}) {
  11. return ExpenseCreateState(
  12. expense: expense ?? this.expense,
  13. isSubmitting: isSubmitting ?? this.isSubmitting,
  14. );
  15. }
  16. }
  17. class ExpenseCreateController extends StateNotifier<ExpenseCreateState> {
  18. final ExpenseApi _api;
  19. ExpenseCreateController(this._api, {ExpenseModel? initial})
  20. : super(
  21. ExpenseCreateState(
  22. expense:
  23. initial ??
  24. ExpenseModel(
  25. id: '',
  26. expenseNo: '',
  27. createTime: DateTime.now(),
  28. updateTime: DateTime.now(),
  29. ),
  30. ),
  31. );
  32. void updateCurrencyCode(String code) {
  33. state = state.copyWith(expense: state.expense.copyWith(currencyCode: code));
  34. }
  35. void updateApprovedAmount(double amount) {
  36. state = state.copyWith(expense: state.expense.copyWith(approvedAmount: amount));
  37. }
  38. void updateDetailApprovedAmount(int index, double amount) {
  39. final details = [...state.expense.details];
  40. details[index] = details[index].copyWith(approvedAmount: amount);
  41. state = state.copyWith(expense: state.expense.copyWith(details: details));
  42. }
  43. void updateDetailCustomerVendor(int index, String name) {
  44. final details = [...state.expense.details];
  45. details[index] = details[index].copyWith(customerVendorName: name);
  46. state = state.copyWith(expense: state.expense.copyWith(details: details));
  47. }
  48. void updateDetailProjectId(int index, String id) {
  49. final details = [...state.expense.details];
  50. details[index] = details[index].copyWith(projectId: id);
  51. state = state.copyWith(expense: state.expense.copyWith(details: details));
  52. }
  53. void updateDetailAcctSubjectId(int index, String id) {
  54. final details = [...state.expense.details];
  55. details[index] = details[index].copyWith(acctSubjectId: id);
  56. state = state.copyWith(expense: state.expense.copyWith(details: details));
  57. }
  58. void updateDetailOffsetAmount(int index, double amount) {
  59. final details = [...state.expense.details];
  60. details[index] = details[index].copyWith(offsetAmount: amount);
  61. state = state.copyWith(expense: state.expense.copyWith(details: details));
  62. }
  63. void restoreFromDraft(ExpenseModel draft, ExpenseApi api) {
  64. state = ExpenseCreateState(expense: draft);
  65. }
  66. void updatePurpose(String purpose) {
  67. state = state.copyWith(expense: state.expense.copyWith(purpose: purpose));
  68. }
  69. void addDetail(ExpenseDetailModel detail) {
  70. final details = [...state.expense.details, detail];
  71. state = state.copyWith(expense: state.expense.copyWith(details: details));
  72. }
  73. void removeDetail(int index) {
  74. final details = [...state.expense.details]..removeAt(index);
  75. state = state.copyWith(expense: state.expense.copyWith(details: details));
  76. }
  77. void recalculateAmount() {
  78. var totalAmount = 0.0;
  79. var approvedAmount = 0.0;
  80. for (final d in state.expense.details) {
  81. totalAmount += d.totalAmount;
  82. approvedAmount += d.approvedAmount;
  83. }
  84. state = state.copyWith(
  85. expense: state.expense.copyWith(
  86. totalAmount: totalAmount,
  87. approvedAmount: approvedAmount,
  88. ),
  89. );
  90. }
  91. Future<bool> submit() async {
  92. state = state.copyWith(isSubmitting: true);
  93. try {
  94. await _api.submit(state.expense.copyWith(status: 'pending').toJson());
  95. await DraftStorage.delete(expenseDraftKey);
  96. return true;
  97. } catch (_) {
  98. return false;
  99. } finally {
  100. state = state.copyWith(isSubmitting: false);
  101. }
  102. }
  103. Future<bool> saveDraft() async {
  104. state = state.copyWith(isSubmitting: true);
  105. try {
  106. await DraftStorage.save(expenseDraftKey, state.expense.toJson());
  107. return true;
  108. } catch (_) {
  109. return false;
  110. } finally {
  111. state = state.copyWith(isSubmitting: false);
  112. }
  113. }
  114. static Future<ExpenseModel?> loadDraft() async {
  115. final data = await DraftStorage.load(expenseDraftKey);
  116. if (data == null) return null;
  117. try {
  118. return ExpenseModel.fromJson(data);
  119. } catch (_) {
  120. return null;
  121. }
  122. }
  123. static Future<bool> hasDraft() => DraftStorage.has(expenseDraftKey);
  124. static Future<void> deleteDraft() => DraftStorage.delete(expenseDraftKey);
  125. }
  126. final expenseCreateProvider = StateNotifierProvider.autoDispose
  127. .family<ExpenseCreateController, ExpenseCreateState, String?>((ref, editId) {
  128. final api = ref.watch(expenseApiProvider);
  129. return ExpenseCreateController(api);
  130. });