@@ -76,14 +76,13 @@ class TransactionDetailsState extends ChangeNotifier {
7676 String ? name;
7777 DateTime ? date;
7878 int ? value;
79- Category ? category;
8079 String ? note;
8180
8281 /// Reimbursement editor
8382 int reimbursementValue = 0 ;
8483
8584 /// Allocation editor
86- final Allocation updatedAllocation = Allocation (name: '' , category: null , value: 0 );
85+ Allocation updatedAllocation = Allocation (name: '' , category: null , value: 0 );
8786
8887 //---------------------------------------------------------------------------------------------
8988 // These variables are the UI state for the relevant fields. They are used to finalize the output
@@ -94,7 +93,9 @@ class TransactionDetailsState extends ChangeNotifier {
9493 /// filter state of the reimbursement targets. It is set on each [onValueChanged] .
9594 ExpenseFilterType expenseType = ExpenseFilterType .all;
9695 TransactionDetailActiveFocus focus = TransactionDetailActiveFocus .none;
96+ Category ? category;
9797 String ? errorMessage;
98+ String ? allocationErrorMessage;
9899 String ? reimbursementError;
99100 bool saveAsRule = false ;
100101
@@ -117,10 +118,24 @@ class TransactionDetailsState extends ChangeNotifier {
117118 tags.insertAll (0 , seed? .tags ?? const []);
118119 allocations.insertAll (0 , seed? .allocations ?? const []);
119120 reimbursements.insertAll (0 , seed? .reimbursements ?? const []);
121+
122+ category = _initialCategory ();
123+
120124 notifyListeners ();
121125 }
122126 }
123127
128+ // the transaction constructor will convert Category.empty into the correct super category
129+ // however we must manually convert the initial category to [Category.empty] so that there isn't
130+ // a duplicate.
131+ Category ? _initialCategory () {
132+ if (seed == null ) return null ;
133+ if (seed! .category == Category .income || seed! .category == Category .expense) {
134+ return Category .empty;
135+ }
136+ return seed! .category;
137+ }
138+
124139 void replaceSeed (Transaction ? t) {
125140 seed = t;
126141 reset ();
@@ -131,6 +146,7 @@ class TransactionDetailsState extends ChangeNotifier {
131146 /// Reset manual state elements
132147 saveAsRule = false ;
133148 errorMessage = null ;
149+ allocationErrorMessage = null ;
134150 reimbursementError = null ;
135151 reimburseTarget = null ;
136152 tags.clear ();
@@ -320,22 +336,42 @@ class TransactionDetailsState extends ChangeNotifier {
320336 }
321337 }
322338
339+ String ? _validateAllocation () {
340+ if (! (allocationFormKey.currentState? .validate () ?? false )) {
341+ return '' ;
342+ }
343+ allocationFormKey.currentState? .save ();
344+
345+ if (updatedAllocation.category == null ) {
346+ if (updatedAllocation.timestamp != null && category != null ) {
347+ updatedAllocation.category = category;
348+ } else {
349+ return 'Category must be selected' ;
350+ }
351+ }
352+ return null ;
353+ }
354+
323355 /// Save a single allocation from the allocation editor in [allocations]
324356 void saveAllocation () {
325- if (allocationFormKey.currentState? .validate () ?? false ) {
326- allocationFormKey.currentState? .save ();
327- if (focusedAllocation == null ) {
328- allocations.add (updatedAllocation.copy ());
329- } else {
330- for (int i = 0 ; i < allocations.length; i++ ) {
331- if (allocations[i] == focusedAllocation) {
332- allocations[i] = updatedAllocation.copy (key: allocations[i].key);
333- break ;
334- }
357+ String ? errorMsg = _validateAllocation ();
358+ if (errorMsg != null ) {
359+ allocationErrorMessage = errorMsg;
360+ notifyListeners ();
361+ return ;
362+ }
363+
364+ if (focusedAllocation == null ) {
365+ allocations.add (updatedAllocation.copy ());
366+ } else {
367+ for (int i = 0 ; i < allocations.length; i++ ) {
368+ if (allocations[i] == focusedAllocation) {
369+ allocations[i] = updatedAllocation.copy (key: allocations[i].key);
370+ break ;
335371 }
336372 }
337- clearFocus ();
338373 }
374+ clearFocus ();
339375 }
340376
341377 /// Validates the form for the reimbursement editor only. Returns an error message, or null on
@@ -417,16 +453,19 @@ class TransactionDetailsState extends ChangeNotifier {
417453 //---------------------------------------------------------------------------------------------
418454 void clearFocus () {
419455 focusedAllocation = null ;
456+ updatedAllocation = Allocation (name: '' , category: null , value: 0 );
457+ allocationErrorMessage = null ;
420458 focusedReimbursement = null ;
421459 reimburseTarget = null ;
422460 focus = TransactionDetailActiveFocus .none;
423461 notifyListeners ();
424462 }
425463
426464 void focusAllocation (Allocation ? alloc) {
427- if (focus == TransactionDetailActiveFocus .reimbursement ) {
428- focusedReimbursement = null ;
465+ if (focus == TransactionDetailActiveFocus .allocation ) {
466+ return ;
429467 }
468+ clearFocus ();
430469 focusedAllocation = alloc;
431470 focus = TransactionDetailActiveFocus .allocation;
432471 resetAllocation ();
@@ -436,9 +475,10 @@ class TransactionDetailsState extends ChangeNotifier {
436475 }
437476
438477 void focusReimbursement (Reimbursement ? it) {
439- if (focus == TransactionDetailActiveFocus .allocation ) {
440- focusedAllocation = null ;
478+ if (focus == TransactionDetailActiveFocus .reimbursement ) {
479+ return ;
441480 }
481+ clearFocus ();
442482 focusedReimbursement = it;
443483 reimburseTarget = it? .target;
444484 focus = TransactionDetailActiveFocus .reimbursement;
@@ -469,6 +509,13 @@ class TransactionDetailsState extends ChangeNotifier {
469509 }
470510 }
471511
512+ void onCategorySelected (Category ? cat) {
513+ if (category != cat) {
514+ category = cat;
515+ notifyListeners ();
516+ }
517+ }
518+
472519 void onTagChanged (Tag tag, bool ? selected) {
473520 if (selected == true ) {
474521 tags.add (tag);
0 commit comments