expense_create_controller.dart 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 reset() {
  67. state = ExpenseCreateState(
  68. expense: ExpenseModel(
  69. id: '',
  70. expenseNo: '',
  71. createTime: DateTime.now(),
  72. updateTime: DateTime.now(),
  73. ),
  74. );
  75. }
  76. void updatePurpose(String purpose) {
  77. state = state.copyWith(expense: state.expense.copyWith(purpose: purpose));
  78. }
  79. ExpenseCreateState get currentState => state;
  80. void updateAttachments(List<String> paths) {
  81. state = state.copyWith(expense: state.expense.copyWith(attachments: paths));
  82. }
  83. void updatePaymentMethod(String method) {
  84. state = state.copyWith(expense: state.expense.copyWith(paymentMethod: method));
  85. }
  86. void updateRemark(String remark) {
  87. state = state.copyWith(expense: state.expense.copyWith(remark: remark));
  88. }
  89. void updateDept(String deptId, String deptName) {
  90. state = state.copyWith(expense: state.expense.copyWith(deptId: deptId, deptName: deptName));
  91. }
  92. void addDetail(ExpenseDetailModel detail) {
  93. final details = [...state.expense.details, detail];
  94. state = state.copyWith(expense: state.expense.copyWith(details: details));
  95. }
  96. void removeDetail(int index) {
  97. final details = [...state.expense.details]..removeAt(index);
  98. state = state.copyWith(expense: state.expense.copyWith(details: details));
  99. }
  100. void updateDetail(int index, ExpenseDetailModel detail) {
  101. final details = [...state.expense.details];
  102. details[index] = detail;
  103. state = state.copyWith(expense: state.expense.copyWith(details: details));
  104. }
  105. void recalculateAmount() {
  106. var totalAmount = 0.0;
  107. var approvedAmount = 0.0;
  108. for (final d in state.expense.details) {
  109. totalAmount += d.totalAmount;
  110. approvedAmount += d.approvedAmount;
  111. }
  112. state = state.copyWith(
  113. expense: state.expense.copyWith(
  114. totalAmount: totalAmount,
  115. approvedAmount: approvedAmount,
  116. ),
  117. );
  118. }
  119. Future<bool> submit() async {
  120. state = state.copyWith(isSubmitting: true);
  121. try {
  122. await _api.submit(state.expense.copyWith(status: 'pending').toJson());
  123. await DraftStorage.delete(expenseDraftKey);
  124. return true;
  125. } catch (_) {
  126. return false;
  127. } finally {
  128. state = state.copyWith(isSubmitting: false);
  129. }
  130. }
  131. Future<bool> saveDraft() async {
  132. state = state.copyWith(isSubmitting: true);
  133. try {
  134. await DraftStorage.save(expenseDraftKey, state.expense.toJson());
  135. return true;
  136. } catch (_) {
  137. return false;
  138. } finally {
  139. state = state.copyWith(isSubmitting: false);
  140. }
  141. }
  142. static Future<ExpenseModel?> loadDraft() async {
  143. final data = await DraftStorage.load(expenseDraftKey);
  144. if (data == null) return null;
  145. try {
  146. return ExpenseModel.fromJson(data);
  147. } catch (_) {
  148. return null;
  149. }
  150. }
  151. static Future<bool> hasDraft() => DraftStorage.has(expenseDraftKey);
  152. static Future<void> deleteDraft() => DraftStorage.delete(expenseDraftKey);
  153. }
  154. final expenseCreateProvider = StateNotifierProvider.autoDispose
  155. .family<ExpenseCreateController, ExpenseCreateState, String?>((ref, editId) {
  156. final api = ref.watch(expenseApiProvider);
  157. return ExpenseCreateController(api);
  158. });