diff --git a/MMExNotifier.Tests/MainViewModelTests.cs b/MMExNotifier.Tests/MainViewModelTests.cs index 9fc9b09..d46e953 100644 --- a/MMExNotifier.Tests/MainViewModelTests.cs +++ b/MMExNotifier.Tests/MainViewModelTests.cs @@ -120,5 +120,33 @@ public void OnDatabaseError_ShouldShowErrorMessage() mockNotificationService.Verify(); } + + + [Test] + public void SavingAppSettings_ShouldReloadExpiringBills() + { + mockDatabaseService.Setup(x => x.ExpiringBills).Returns( + new List + { + new() + { + BillId=1, + CategoryName="testCategory", + PayeeName="TestPayee", + NextOccurrenceDate=new DateTime(2024,6,1) + } + }); + + mockNotificationService.Setup( + x => x.ShowErrorNotification(It.IsAny()) + ); + + var mainViewModel = new MainViewModel(mockAppConfiguration.Object, mockNotificationService.Object, mockDatabaseService.Object); + mainViewModel.Activate(); + mainViewModel.SaveSettingsCommand.Execute(null); + + mockNotificationService.Verify(); + mockDatabaseService.Verify(x => x.ExpiringBills, Times.Exactly(2)); + } } } \ No newline at end of file diff --git a/MMExNotifier/App.xaml b/MMExNotifier/App.xaml index 2a46e2e..69b5555 100644 --- a/MMExNotifier/App.xaml +++ b/MMExNotifier/App.xaml @@ -1,8 +1,15 @@ - + - + + + + + + diff --git a/MMExNotifier/DataModel/ExpiringBill.cs b/MMExNotifier/DataModel/ExpiringBill.cs index 8fbf123..e2bd593 100644 --- a/MMExNotifier/DataModel/ExpiringBill.cs +++ b/MMExNotifier/DataModel/ExpiringBill.cs @@ -7,9 +7,12 @@ public class ExpiringBill public int BillId { get; set; } public DateTime NextOccurrenceDate { get; set; } public int DaysToNextOccurrence => (int)NextOccurrenceDate.Subtract(DateTime.Today).TotalDays; + public bool IsExpired => DaysToNextOccurrence < 0; public string? PayeeName { get; set; } public string? CategoryName { get; set; } public string? SubCategoryName { get; set; } public string? Notes { get; set; } + public int Amount { get; set; } + public bool IsDeposit { get; set; } } } diff --git a/MMExNotifier/Database/DatabaseService.cs b/MMExNotifier/Database/DatabaseService.cs index ceb3773..861ce7e 100644 --- a/MMExNotifier/Database/DatabaseService.cs +++ b/MMExNotifier/Database/DatabaseService.cs @@ -42,6 +42,8 @@ orderby b.NEXTOCCURRENCEDATE BillId = b.BDID, NextOccurrenceDate = b.NEXTOCCURRENCEDATE.Value, PayeeName = p.PAYEENAME!, + Amount = b.TOTRANSAMOUNT, + IsDeposit = b.TRANSCODE == "Deposit" ? true : false, CategoryName = c.CATEGNAME!, SubCategoryName = s.CATEGNAME!, Notes = b.NOTES! diff --git a/MMExNotifier/MMExNotifier.csproj b/MMExNotifier/MMExNotifier.csproj index b297fc8..0c71a4c 100644 --- a/MMExNotifier/MMExNotifier.csproj +++ b/MMExNotifier/MMExNotifier.csproj @@ -6,23 +6,33 @@ enable true app.manifest + Resources\MMExNotifier.ico + 1.1.0.0 + 1.1.0.0 + 1.1.0 - + + + + + + - + - + + diff --git a/MMExNotifier/MVVM/DaysToTextConverter.cs b/MMExNotifier/MVVM/DaysToTextConverter.cs new file mode 100644 index 0000000..e1500f7 --- /dev/null +++ b/MMExNotifier/MVVM/DaysToTextConverter.cs @@ -0,0 +1,47 @@ +using MMExNotifier.DataModel; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace MMExNotifier.MVVM +{ + internal class DaysToTextConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null || value is not ExpiringBill) + return string.Empty; + + var expiringBill = (ExpiringBill)value; + + if (expiringBill.DaysToNextOccurrence < -5) + return $"expired on {expiringBill.NextOccurrenceDate:d} ({-expiringBill.DaysToNextOccurrence} days ago)!"; + + if (expiringBill.DaysToNextOccurrence < -1) + return $"expired {-expiringBill.DaysToNextOccurrence} days ago!"; + + if (expiringBill.DaysToNextOccurrence == -1) + return "expired yesterday!"; + + if (expiringBill.DaysToNextOccurrence == 0) + return "expires today."; + + if (expiringBill.DaysToNextOccurrence == 1) + return "expires tomorrow."; + + if (expiringBill.DaysToNextOccurrence > 1) + return $"will expire on {expiringBill.NextOccurrenceDate:d} (in {expiringBill.DaysToNextOccurrence} days)."; + + return string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/MMExNotifier/MVVM/DerivableDynamicStyle.cs b/MMExNotifier/MVVM/DerivableDynamicStyle.cs new file mode 100644 index 0000000..8e83610 --- /dev/null +++ b/MMExNotifier/MVVM/DerivableDynamicStyle.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace MMExNotifier.MVVM +{ + internal class DerivableDynamicStyle + { + public static Style GetBaseStyle(DependencyObject obj) + { + return (Style)obj.GetValue(BaseStyleProperty); + } + + public static void SetBaseStyle(DependencyObject obj, Style value) + { + obj.SetValue(BaseStyleProperty, value); + } + + public static readonly DependencyProperty BaseStyleProperty = + DependencyProperty.RegisterAttached("BaseStyle", typeof(Style), typeof(FrameworkElement), new UIPropertyMetadata(StyleChanged)); + + public static Style GetDerivedStyle(DependencyObject obj) + { + return (Style)obj.GetValue(DerivedStyleProperty); + } + + public static void SetDerivedStyle(DependencyObject obj, Style value) + { + obj.SetValue(DerivedStyleProperty, value); + } + + public static readonly DependencyProperty DerivedStyleProperty = + DependencyProperty.RegisterAttached("DerivedStyle", typeof(Style), typeof(FrameworkElement), new UIPropertyMetadata(StyleChanged)); + + + private static void StyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Style baseStyle = GetBaseStyle(d); + Style derivedStyle = GetDerivedStyle(d); + Style newStyle; + + if (derivedStyle == null) + { + newStyle = baseStyle; + } + else + { + newStyle = new Style { BasedOn = baseStyle, TargetType = derivedStyle.TargetType }; + + foreach (var setter in derivedStyle.Setters) + { + newStyle.Setters.Add(setter); + } + + foreach (var trigger in derivedStyle.Triggers) + { + newStyle.Triggers.Add(trigger); + } + } + + var fe = (FrameworkElement)d; + fe.Style = newStyle; + } + } +} diff --git a/MMExNotifier/Resources/MMExNotifier.ico b/MMExNotifier/Resources/MMExNotifier.ico new file mode 100644 index 0000000..8ebb837 Binary files /dev/null and b/MMExNotifier/Resources/MMExNotifier.ico differ diff --git a/MMExNotifier/Resources/MMExNotifier.png b/MMExNotifier/Resources/MMExNotifier.png new file mode 100644 index 0000000..a186387 Binary files /dev/null and b/MMExNotifier/Resources/MMExNotifier.png differ diff --git a/MMExNotifier/Resources/options.png b/MMExNotifier/Resources/options.png deleted file mode 100644 index 61089dc..0000000 Binary files a/MMExNotifier/Resources/options.png and /dev/null differ diff --git a/MMExNotifier/ViewModels/Design/MainViewModel.cs b/MMExNotifier/ViewModels/Design/MainViewModel.cs new file mode 100644 index 0000000..cc0b16e --- /dev/null +++ b/MMExNotifier/ViewModels/Design/MainViewModel.cs @@ -0,0 +1,37 @@ +using MMExNotifier.DataModel; +using MMExNotifier.MVVM; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MMExNotifier.ViewModels.Design +{ + internal class MainViewModel : ViewModelBase + { + public RangeObservableCollection ExpiringBills { get; set; } = new RangeObservableCollection(); + + public MainViewModel() + { + ExpiringBills = new RangeObservableCollection() { + new () + { + BillId = 1, + CategoryName = "Category Name", + SubCategoryName = "Subcategory Name", + PayeeName = "Payee Name", + NextOccurrenceDate = new DateTime(2024, 10, 30), + Notes = "some notes about this entry.", + } + }; + + OnPropertyChanged(nameof(ExpiringBills)); + } + + public override void Activate() + { + throw new NotImplementedException(); + } + } +} diff --git a/MMExNotifier/ViewModels/MainViewModel.cs b/MMExNotifier/ViewModels/MainViewModel.cs index 01b0235..4961f93 100644 --- a/MMExNotifier/ViewModels/MainViewModel.cs +++ b/MMExNotifier/ViewModels/MainViewModel.cs @@ -39,7 +39,7 @@ public override void Activate() } const int ConversationId = 9813; - _notificationService.ShowToastNotification("viewTransactions", ConversationId, "MMExNotifier", "One ore more recurring transaction are about to expire.", () => Open()); + _notificationService.ShowToastNotification("viewTransactions", ConversationId, "MMExNotifier", "One or more recurring transaction are about to expire.", () => Open()); } private void LoadExpiringBills() diff --git a/MMExNotifier/Views/MainWindow.xaml b/MMExNotifier/Views/MainWindow.xaml index d57e9f0..45e8242 100644 --- a/MMExNotifier/Views/MainWindow.xaml +++ b/MMExNotifier/Views/MainWindow.xaml @@ -1,165 +1,238 @@ - + + + - + + - + + + + + + - + + + + + + + + + - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - + + - - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -179,49 +252,84 @@ - - + + - - - + - - - - - - - + + + + + + + -