diff --git a/Financier.Desktop.gif b/Financier.Desktop.gif
index 9469ac62..00b9d343 100644
Binary files a/Financier.Desktop.gif and b/Financier.Desktop.gif differ
diff --git a/README.md b/README.md
index 7c4b0767..dac03608 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,34 @@
-| What | Badge|
-| ---- | ---- |
-| Build | [![Build Status](https://dev.azure.com/khmelovskyi/Financier.Desktop/_apis/build/status/vov4uk.Financier.Desktop?branchName=master)](https://dev.azure.com/khmelovskyi/Financier.Desktop/_build/latest?definitionId=2&branchName=master)|
-| Latest | [![GitHub (pre-)release](https://img.shields.io/github/v/release/vov4uk/financier.desktop?include_prereleases)](https://github.com/vov4uk/Financier.Desktop/releases)|
-| Total| [![Github Releases](https://img.shields.io/github/downloads/vov4uk/Financier.Desktop/total)](https://github.com/vov4uk/Financier.Desktop/releases)|
-| Sonar | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vov4uk_Financier.Desktop&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vov4uk_Financier.Desktop)|
-
-# Financier Desktop
-[**Install the .NET Desktop Runtime.**](https://dotnet.microsoft.com/en-us/download/dotnet/6.0/runtime)
-
-MVP (minimum viable product) application, with enough functions to work with Financier .backup on Windows.
-More info on [Github pages](https://vov4uk.github.io/Financier.Desktop/)
+
Financier Desktop
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[**Install the .NET Desktop Runtime.**](https://dotnet.microsoft.com/en-us/download/dotnet/8.0/runtime)
+
+MVP (minimum viable product) application, with enough functions to work with Financisto .backup on Windows.
+More info on [Github pages](https://vov4uk.github.io/Financier.Desktop)
## About
Desktop version of [Financier](https://github.com/handydevcom/financier "Financier") which is a fork of the great [Financisto](https://github.com/dsolonenko/financisto) app. Financisto is an open-source personal finance tracker for Android platform.
## Supported features
-- Open/save Financier (Financisto) .backup files
+- Open/save Financisto .backup files
- View transactions list
- Transactions search and filtering
- Edit transactions/transfers
@@ -24,6 +36,7 @@ More info on [Github pages](https://vov4uk.github.io/Financier.Desktop/)
- Edit entities (projects, locations, payees...)
- Import CSV report from Monobank
- Import PDF report from A-Bank
+- Import PDF report from PUMB
- Parse recipiet text to SPLIT transaction
- Save .backup as SQLite database (.db file)
- Reporting (kudos to [@2ruslan](https://github.com/2ruslan))
diff --git a/src/Financier.Adapter/BackupReader.cs b/src/Financier.Adapter/BackupReader.cs
index 76a76f96..7ec41b45 100644
--- a/src/Financier.Adapter/BackupReader.cs
+++ b/src/Financier.Adapter/BackupReader.cs
@@ -39,7 +39,7 @@ private void ReadHeader()
break;
case Backup.VERSION_NAME:
- BackupVersion.Version = Version.Parse(line.Value);
+ BackupVersion.Version = line.Value;
break;
case Backup.DATABASE_VERSION:
diff --git a/src/Financier.Adapter/BackupVersion.cs b/src/Financier.Adapter/BackupVersion.cs
index c3e0d606..56254c56 100644
--- a/src/Financier.Adapter/BackupVersion.cs
+++ b/src/Financier.Adapter/BackupVersion.cs
@@ -6,7 +6,7 @@ public class BackupVersion
{
public string Package { get; set; }
public int VersionCode { get; set; }
- public Version Version { get; set; }
+ public string Version { get; set; }
public int DatabaseVersion { get; set; }
}
}
diff --git a/src/Financier.Common/Assets/Generic.xaml b/src/Financier.Common/Assets/Generic.xaml
index 12b40fdd..fa59169f 100644
--- a/src/Financier.Common/Assets/Generic.xaml
+++ b/src/Financier.Common/Assets/Generic.xaml
@@ -34,13 +34,13 @@
diff --git a/src/Financier.Common/Filters/PeriodFilter.xaml.cs b/src/Financier.Common/Filters/PeriodFilter.xaml.cs
index 09a5a86a..24c36dcd 100644
--- a/src/Financier.Common/Filters/PeriodFilter.xaml.cs
+++ b/src/Financier.Common/Filters/PeriodFilter.xaml.cs
@@ -88,8 +88,8 @@ private static (DateTime? from, DateTime? to) UpdatePeriod(PeriodType type, Date
{
var today = DateTime.Today;
- from = new DateTime(today.AddMonths(-1).Year, today.AddMonths(-1).Month, 1);
- to = new DateTime(today.Year, today.Month, 1).AddMilliseconds(-1);
+ from = new DateTime(today.AddMonths(-1).Year, today.AddMonths(-1).Month, 1,0,0,0, DateTimeKind.Local);
+ to = new DateTime(today.Year, today.Month, 1,0,0,0, DateTimeKind.Local).AddMilliseconds(-1);
}
break;
case PeriodType.CurrentWeek:
@@ -108,8 +108,8 @@ private static (DateTime? from, DateTime? to) UpdatePeriod(PeriodType type, Date
{
var today = DateTime.Today;
- from = new DateTime(today.Year, today.Month, 1);
- to = new DateTime(today.AddMonths(1).Year, today.AddMonths(1).Month, 1).AddMilliseconds(-1);
+ from = new DateTime(today.Year, today.Month, 1, 0, 0, 0, DateTimeKind.Local);
+ to = new DateTime(today.AddMonths(1).Year, today.AddMonths(1).Month, 1, 0, 0, 0, DateTimeKind.Local).AddMilliseconds(-1);
}
break;
case PeriodType.PreviousAndCurrentWeek:
@@ -128,8 +128,8 @@ private static (DateTime? from, DateTime? to) UpdatePeriod(PeriodType type, Date
{
var today = DateTime.Today;
- from = new DateTime(today.AddMonths(-1).Year, today.AddMonths(-1).Month, 1);
- to = new DateTime(today.AddMonths(1).Year, today.AddMonths(1).Month, 1).AddMilliseconds(-1);
+ from = new DateTime(today.AddMonths(-1).Year, today.AddMonths(-1).Month, 1, 0, 0, 0, DateTimeKind.Local);
+ to = new DateTime(today.AddMonths(1).Year, today.AddMonths(1).Month, 1, 0, 0, 0, DateTimeKind.Local).AddMilliseconds(-1);
}
break;
default:
diff --git a/src/Financier.Common/Financier.Common.csproj b/src/Financier.Common/Financier.Common.csproj
index a3cf18cd..8c58776e 100644
--- a/src/Financier.Common/Financier.Common.csproj
+++ b/src/Financier.Common/Financier.Common.csproj
@@ -1,7 +1,7 @@
- net6.0-windows
+ net8.0-windows7.0
enable
true
diff --git a/src/Financier.DataAccess/Abstractions/IBaseRepository.cs b/src/Financier.DataAccess/Abstractions/IBaseRepository.cs
index 3717c54c..9ab1f859 100644
--- a/src/Financier.DataAccess/Abstractions/IBaseRepository.cs
+++ b/src/Financier.DataAccess/Abstractions/IBaseRepository.cs
@@ -24,7 +24,7 @@ public interface IBaseRepository where T : class
Task> FindManyAsync(Expression> predicate, params Expression>[] includes);
- Task> FindManyAsync(
+ Task> FindManyAndProjectAsync(
Expression> predicate,
Expression> projection,
params Expression>[] includes);
diff --git a/src/Financier.DataAccess/BaseRepository.cs b/src/Financier.DataAccess/BaseRepository.cs
index f021da28..fb4825c5 100644
--- a/src/Financier.DataAccess/BaseRepository.cs
+++ b/src/Financier.DataAccess/BaseRepository.cs
@@ -78,7 +78,7 @@ public virtual async Task> FindManyAsync(Expression> predi
return await result?.ToListAsync();
}
- public virtual async Task> FindManyAsync(
+ public virtual async Task> FindManyAndProjectAsync(
Expression> predicate,
Expression> projection,
params Expression>[] includes)
diff --git a/src/Financier.DataAccess/FinancierDatabase.cs b/src/Financier.DataAccess/FinancierDatabase.cs
index 07018a2c..ccbed5d9 100644
--- a/src/Financier.DataAccess/FinancierDatabase.cs
+++ b/src/Financier.DataAccess/FinancierDatabase.cs
@@ -147,11 +147,14 @@ public async Task RebuildAccountBalanceAsync(int accountId)
}
var acc = context.Accounts.FirstOrDefault(x => x.Id == accountId);
- acc.TotalAmount = balance;
var lastTransaction = transactions.LastOrDefault();
- acc.LastTransactionDate = lastTransaction?.DateTime ?? 0;
- acc.LastTransactionId = lastTransaction?.Id ?? 0;
- context.Accounts.Update(acc);
+ if (acc != null)
+ {
+ acc.TotalAmount = balance;
+ acc.LastTransactionDate = lastTransaction?.DateTime ?? 0;
+ acc.LastTransactionId = lastTransaction?.Id ?? 0;
+ context.Accounts.Update(acc);
+ }
await context.SaveChangesAsync();
}
}
@@ -197,9 +200,17 @@ public async Task> GetSubTransactionsAsync(int id)
if (id != 0)
{
using var uow = CreateUnitOfWork();
- return (await uow.GetRepository().FindManyAsync(
- predicate: x => x.ParentId == id,
- includes: new Expression>[]{ o => o.OriginalCurrency, c => c.Category})) ?? Array.Empty().ToList();
+ var subTransactions = await uow.GetRepository().
+ FindManyAsync(
+ x => x.ParentId == id,
+ o => o.OriginalCurrency,
+ c => c.Category);
+
+ if (subTransactions == null)
+ {
+ return Array.Empty();
+ }
+ return subTransactions;
}
return Array.Empty();
@@ -250,7 +261,7 @@ public async Task InsertOrUpdateAsync(IEnumerable entities)
int ordinal = reader.GetOrdinal(customAttribute.Name);
object obj = ordinal != -1 ?
reader.GetValue(ordinal) :
- throw new Exception(string.Format("Class [{0}] have attribute of field [{1}] which not exist in reader", this.GetType(), customAttribute.Name));
+ throw new InvalidCastException(string.Format("Class [{0}] have attribute of field [{1}] which not exist in reader", this.GetType(), customAttribute.Name));
if (obj != DBNull.Value)
{
diff --git a/src/Financier.Desktop/Data/BaseTransactionDTO.cs b/src/Financier.Desktop/Data/BaseTransactionDTO.cs
index ca64aa01..d702a5cf 100644
--- a/src/Financier.Desktop/Data/BaseTransactionDTO.cs
+++ b/src/Financier.Desktop/Data/BaseTransactionDTO.cs
@@ -37,7 +37,7 @@ public DateTime Time
public DateTime DateTime
{
- get { return new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second); }
+ get { return new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second, DateTimeKind.Local); }
}
public int Id
diff --git a/src/Financier.Desktop/Data/TransactionDTO.cs b/src/Financier.Desktop/Data/TransactionDTO.cs
index 06b9f765..2ea7ce4a 100644
--- a/src/Financier.Desktop/Data/TransactionDTO.cs
+++ b/src/Financier.Desktop/Data/TransactionDTO.cs
@@ -56,7 +56,14 @@ public TransactionDto(Transaction transaction, IEnumerable subTrans
}
else
{
- list.Add(new TransactionDto(t));
+ var tr = new TransactionDto(t);
+
+ // if transaction not in home currency, replace FromAmount with OriginalFromAmount to show correct values
+ if (IsOriginalFromAmountVisible)
+ {
+ tr.FromAmount = tr.OriginalFromAmount ?? 0;
+ }
+ list.Add(tr);
}
}
@@ -83,7 +90,7 @@ public TransactionDto(Transaction transaction)
public AccountFilterModel FromAccount
{
- get => fromAccount ??= DbManual.Account?.FirstOrDefault(x => x.Id == FromAccountId);
+ get => fromAccount ??= DbManual.Account?.Find(x => x.Id == FromAccountId);
set
{
if (SetProperty(ref fromAccount, value))
@@ -110,12 +117,12 @@ public int FromAccountId
public CurrencyModel FromAccountCurrency
{
- get => DbManual.Currencies?.FirstOrDefault(x => x.Id == (FromAccount != null ? FromAccount.CurrencyId : 0));
+ get => DbManual.Currencies?.Find(x => x.Id == (FromAccount != null ? FromAccount.CurrencyId : 0));
}
public CategoryModel Category
{
- get => category ??= DbManual.Category?.FirstOrDefault(x => x.Id == CategoryId);
+ get => category ??= DbManual.Category?.Find(x => x.Id == CategoryId);
set
{
if (SetProperty(ref category, value))
@@ -187,7 +194,7 @@ public int? LocationId
public CurrencyModel OriginalCurrency
{
- get => currency ??= DbManual.Currencies?.FirstOrDefault(x => x.Id == OriginalCurrencyId);
+ get => currency ??= DbManual.Currencies?.Find(x => x.Id == OriginalCurrencyId);
set
{
if (SetProperty(ref currency, value))
@@ -268,7 +275,7 @@ public string RateString
if (Rate != 0)
{
var d = 1.0 / Rate;
- var localCurrency = DbManual.Currencies?.FirstOrDefault(x => x.Id == fromAccount?.Id);
+ var localCurrency = DbManual.Currencies?.Find(x => x.Id == fromAccount?.Id);
return $"1{currency?.Name}={Rate:F5}{localCurrency?.Name}, 1{localCurrency?.Name}={d:F5}{currency?.Name}";
}
@@ -322,7 +329,9 @@ public void RecalculateUnSplitAmount()
internal void RecalculateRate()
{
if (originalFromAmount != null && originalFromAmount != 0)
+ {
Rate = Math.Abs(fromAmount / 100.0 / (originalFromAmount.Value / 100.0));
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Financier.Desktop/Data/TransferDTO.cs b/src/Financier.Desktop/Data/TransferDTO.cs
index 1cfa54d6..33335717 100644
--- a/src/Financier.Desktop/Data/TransferDTO.cs
+++ b/src/Financier.Desktop/Data/TransferDTO.cs
@@ -29,8 +29,8 @@ public TransferDto(Transaction transaction)
toAmount = transaction.ToAmount;
date = UnixTimeConverter.Convert(transaction.DateTime).Date;
time = UnixTimeConverter.Convert(transaction.DateTime);
- fromAccount = DbManual.Account.FirstOrDefault(x => x.Id == fromAccountId);
- toAccount = DbManual.Account.FirstOrDefault(x => x.Id == toAccountId);
+ fromAccount = DbManual.Account.Find(x => x.Id == fromAccountId);
+ toAccount = DbManual.Account.Find(x => x.Id == toAccountId);
}
public AccountFilterModel FromAccount
@@ -50,7 +50,7 @@ public AccountFilterModel FromAccount
public CurrencyModel FromAccountCurrency
{
- get => DbManual.Currencies?.FirstOrDefault(x => x.Id == (FromAccount != null ? FromAccount.CurrencyId : 0));
+ get => DbManual.Currencies?.Find(x => x.Id == (FromAccount != null ? FromAccount.CurrencyId : 0));
}
public int FromAccountId
@@ -126,7 +126,7 @@ public int ToAccountId
public CurrencyModel ToAccountCurrency
{
- get => DbManual.Currencies?.FirstOrDefault(x => x.Id == (ToAccount != null ? ToAccount.CurrencyId : 0));
+ get => DbManual.Currencies?.Find(x => x.Id == (ToAccount != null ? ToAccount.CurrencyId : 0));
}
public long ToAmount
diff --git a/src/Financier.Desktop/Financier.Desktop.csproj b/src/Financier.Desktop/Financier.Desktop.csproj
index 7a522573..590dff7d 100644
--- a/src/Financier.Desktop/Financier.Desktop.csproj
+++ b/src/Financier.Desktop/Financier.Desktop.csproj
@@ -2,7 +2,7 @@
WinExe
- net6.0-windows
+ net8.0-windows7.0
true
true
Images\ic_launcher.ico
@@ -14,10 +14,7 @@
-
-
-
-
+
PreserveNewest
@@ -92,16 +89,16 @@
-
-
-
+
+
+
-
+
-
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Financier.Desktop/Images/folder.png b/src/Financier.Desktop/Images/folder.png
new file mode 100644
index 00000000..4ffd333c
Binary files /dev/null and b/src/Financier.Desktop/Images/folder.png differ
diff --git a/src/Financier.Desktop/MainWindow.xaml b/src/Financier.Desktop/MainWindow.xaml
index d074fce5..bbcbdb44 100644
--- a/src/Financier.Desktop/MainWindow.xaml
+++ b/src/Financier.Desktop/MainWindow.xaml
@@ -52,24 +52,56 @@
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+ DataContext="{Binding}"
+ Visibility="{Binding Path=IsTransactionPageSelected, Converter={StaticResource BoolToVis}, Mode=OneWay}"
+ Header="Transaction">
@@ -198,9 +265,9 @@
+ Selector.IsSelected="{Binding Path=IsProjectPageSelected, Mode=OneWay}"
+ Visibility="{Binding Path=IsProjectPageSelected, Converter={StaticResource BoolToVis}, Mode=OneWay}"
+ DataContext="{Binding}">
@@ -254,7 +321,7 @@
-
+
\ No newline at end of file
diff --git a/src/Financier.Desktop/MainWindow.xaml.cs b/src/Financier.Desktop/MainWindow.xaml.cs
index 72d61ddd..ed7e3076 100644
--- a/src/Financier.Desktop/MainWindow.xaml.cs
+++ b/src/Financier.Desktop/MainWindow.xaml.cs
@@ -12,6 +12,8 @@
using Financier.Common.Model;
using System.Reflection;
using System.Threading.Tasks;
+using Microsoft.Win32;
+using Financier.Desktop.Properties;
namespace Financier.Desktop
{
@@ -45,7 +47,8 @@ public MainWindow()
private void RibbonWindow_Loaded(object sender, RoutedEventArgs e)
{
- var bakupFolder = @$"C:\Users\{Environment.UserName}\Dropbox\apps\FinancierAndroid";
+ var bakupFolder = Settings.Default.DefaultBackupDir ?? @$"C:\Users\{Environment.UserName}\Dropbox\apps\Financisto Holo";
+ ViewModel.DefaultBackupDirectory = Settings.Default.DefaultBackupDir;
if (Directory.Exists(bakupFolder))
{
var backupFile = Directory.EnumerateFiles(bakupFolder, BackupFormat).OrderByDescending(x => x).FirstOrDefault();
@@ -74,5 +77,21 @@ private void Exit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
+
+ private void RibbonApplicationMenuItem_Click(object sender, RoutedEventArgs e)
+ {
+ Microsoft.Win32.OpenFolderDialog openFileDialog = new OpenFolderDialog
+ {
+ Multiselect = false
+ };
+
+ if (openFileDialog.ShowDialog() == true)
+ {
+ var currentFolder = openFileDialog.FolderName;
+ Settings.Default.DefaultBackupDir = currentFolder;
+ Settings.Default.Save();
+ ViewModel.DefaultBackupDirectory = currentFolder;
+ }
+ }
}
}
diff --git a/src/Financier.Desktop/MainWindowVM.cs b/src/Financier.Desktop/MainWindowVM.cs
index 720f58a1..087bd879 100644
--- a/src/Financier.Desktop/MainWindowVM.cs
+++ b/src/Financier.Desktop/MainWindowVM.cs
@@ -49,6 +49,7 @@ public class MainWindowVM : BindableBase
private readonly IEntityReader entityReader;
private LocationsVM locationsVm;
private string openBackupPath;
+ private string defaultBackupDirectory;
private bool isLoading;
private PayeesVM payeesVm;
private ProjectsVM projectsVm;
@@ -103,6 +104,11 @@ public string OpenBackupPath
get => openBackupPath;
private set => SetProperty(ref openBackupPath, value);
}
+ public string DefaultBackupDirectory
+ {
+ get => defaultBackupDirectory;
+ internal set => SetProperty(ref defaultBackupDirectory, value);
+ }
public LocationsVM Locations
{
@@ -250,7 +256,7 @@ private BindableBase GetOrCreatePage(Type type)
return _pages[type];
}
- default: throw new NotSupportedException($"{type.FullName} not suported");
+ default: throw new NotSupportedException($"{type.FullName} not supported");
}
}
@@ -287,9 +293,9 @@ private void OpenBackup_Click()
private async Task OpenImportWizardAsync(WizardTypes bankType)
{
- var fileExtention = EnumDescriptionConverter.GetEnumDescription(bankType);
- var fileName = dialogWrapper.OpenFileDialog(fileExtention);
- Logger.Info($"{fileExtention} fileName -> {fileName}");
+ var fileExtension = EnumDescriptionConverter.GetEnumDescription(bankType);
+ var fileName = dialogWrapper.OpenFileDialog(fileExtension);
+ Logger.Info($"{fileExtension} fileName -> {fileName}");
if (!string.IsNullOrEmpty(fileName))
{
var importHelper = this.bankFactory.CreateBankHelper(bankType);
@@ -329,7 +335,7 @@ private async Task OpenImportWizardAsync(WizardTypes bankType)
this.dialogWrapper.ShowMessageBox(
$"Imported {monoToImport.Count} transactions."
- + ((duplicatesCount > 0) ? $" Skiped {duplicatesCount} duplicates." : string.Empty),
+ + ((duplicatesCount > 0) ? $" Skipped {duplicatesCount} duplicates." : string.Empty),
$"{importHelper.BankTitle} Import");
Logger.Info($"Imported {monoToImport.Count} transactions. Found duplicates : {duplicatesCount}");
diff --git a/src/Financier.Desktop/Pages/AccountsVM.cs b/src/Financier.Desktop/Pages/AccountsVM.cs
index b387439c..2f2ca807 100644
--- a/src/Financier.Desktop/Pages/AccountsVM.cs
+++ b/src/Financier.Desktop/Pages/AccountsVM.cs
@@ -27,7 +27,7 @@ protected override async Task RefreshData()
{
using var uow = db.CreateUnitOfWork();
var accountRepo = uow.GetRepository();
- var items = await accountRepo.FindManyAsync(
+ var items = await accountRepo.FindManyAndProjectAsync(
predicate: x => true,
projection: acc => new AccountModel(acc),
includes : x => x.Currency);
diff --git a/src/Financier.Desktop/Pages/BlotterVM.cs b/src/Financier.Desktop/Pages/BlotterVM.cs
index 9c7d2017..9c218654 100644
--- a/src/Financier.Desktop/Pages/BlotterVM.cs
+++ b/src/Financier.Desktop/Pages/BlotterVM.cs
@@ -80,7 +80,7 @@ public PeriodType PeriodType
public AccountFilterModel Account
{
- get => _account ??= DbManual.Account.FirstOrDefault(p => !p.Id.HasValue);
+ get => _account ??= DbManual.Account.Find(p => !p.Id.HasValue);
set
{
_account = value;
@@ -90,7 +90,7 @@ public AccountFilterModel Account
public CategoryModel Category
{
- get => _category ??= DbManual.Category.FirstOrDefault(p => !p.Id.HasValue);
+ get => _category ??= DbManual.Category.Find(p => !p.Id.HasValue);
set
{
_category = value;
@@ -100,7 +100,7 @@ public CategoryModel Category
public PayeeModel Payee
{
- get => _payee ??= DbManual.Payee.FirstOrDefault(p => !p.Id.HasValue);
+ get => _payee ??= DbManual.Payee.Find(p => !p.Id.HasValue);
set
{
_payee = value;
@@ -110,7 +110,7 @@ public PayeeModel Payee
public ProjectModel Project
{
- get => _project ??= DbManual.Project.FirstOrDefault(p => !p.Id.HasValue);
+ get => _project ??= DbManual.Project.Find(p => !p.Id.HasValue);
set
{
_project = value;
@@ -120,7 +120,7 @@ public ProjectModel Project
public LocationModel Location
{
- get => _location ??= DbManual.Location.FirstOrDefault(p => !p.Id.HasValue);
+ get => _location ??= DbManual.Location.Find(p => !p.Id.HasValue);
set
{
_location = value;
@@ -175,32 +175,41 @@ private async Task DeleteTransaction(int id)
}
}
- protected override Task OnEdit(BlotterModel item)
+ protected override async Task OnEdit(BlotterModel item)
{
if (item.Type == "Transfer")
{
- return OpenTransferDialogAsync(item.Id, false);
+ var transfer = await GetTransfer(item.Id);
+ await OpenTransferDialogAsync(transfer);
}
else
{
- return OpenTransactionDialogAsync(item.Id, false);
+ var t = await GetTransaction(item.Id);
+ await OpenTransactionDialogAsync(t.transaction, t.subTransactions);
}
}
- private Task AddTransfer()
+ private async Task AddTransfer()
{
- return OpenTransferDialogAsync(0, false);
+ Transaction transfer = await db.GetOrCreateTransactionAsync(0);
+ if (Account?.Id != null)
+ {
+ transfer.FromAccountId = (int)Account.Id;
+ }
+ await OpenTransferDialogAsync(transfer);
}
- private Task OnDuplicate(BlotterModel item)
+ private async Task OnDuplicate(BlotterModel item)
{
if (item.Type == "Transfer")
{
- return OpenTransferDialogAsync(item.Id, true);
+ var transfer = await GetTransfer(item.Id, true);
+ await OpenTransferDialogAsync(transfer);
}
else
{
- return OpenTransactionDialogAsync(item.Id, true);
+ var t = await GetTransaction(item.Id, true);
+ await OpenTransactionDialogAsync(t.transaction, t.subTransactions);
}
}
@@ -210,25 +219,21 @@ protected override void OnSelectedValueChanged()
base.OnSelectedValueChanged();
}
- protected override Task OnAdd()
+ protected override async Task OnAdd()
{
- return OpenTransactionDialogAsync(0, false);
- }
+ Transaction transaction = await db.GetOrCreateTransactionAsync(0);
+ IEnumerable subTransactions = await db.GetSubTransactionsAsync(0);
- private async Task OpenTransferDialogAsync(int id, bool isDuplicate)
- {
- Logger.Info($"OpenTransferDialog : id {id} ; isDuplicate {isDuplicate}");
- Transaction transfer = await db.GetOrCreateTransactionAsync(id);
- if (isDuplicate)
- {
- transfer.Id = 0;
- transfer.DateTime = UnixTimeConverter.ConvertBack(DateTime.Now);
- }
- if (id == 0 && Account?.Id != null)
+ if (Account?.Id != null)
{
- transfer.FromAccountId = (int)Account.Id;
+ transaction.FromAccountId = (int)Account.Id;
}
+ await OpenTransactionDialogAsync(transaction, subTransactions);
+ }
+
+ private async Task OpenTransferDialogAsync(Transaction transfer)
+ {
TransferControlVM dialogVm = new TransferControlVM(new TransferDto(transfer));
var result = dialogWrapper.ShowDialog(dialogVm, 385, 340, "Transfer");
@@ -245,9 +250,20 @@ private async Task OpenTransferDialogAsync(int id, bool isDuplicate)
}
}
- private async Task OpenTransactionDialogAsync(int id, bool isDuplicate)
+ private async Task GetTransfer(int id, bool isDuplicate = false)
+ {
+ Transaction transfer = await db.GetOrCreateTransactionAsync(id);
+ if (isDuplicate)
+ {
+ transfer.Id = 0;
+ transfer.DateTime = UnixTimeConverter.ConvertBack(DateTime.Now);
+ }
+
+ return transfer;
+ }
+
+ private async Task<(Transaction transaction, IEnumerable subTransactions)> GetTransaction(int id, bool isDuplicate = false)
{
- Logger.Info($"OpenTransactionDialog: id {id} ; isDuplicate {isDuplicate}");
Transaction transaction = await db.GetOrCreateTransactionAsync(id);
IEnumerable subTransactions = await db.GetSubTransactionsAsync(id);
@@ -256,21 +272,13 @@ private async Task OpenTransactionDialogAsync(int id, bool isDuplicate)
transaction.Id = 0;
transaction.DateTime = UnixTimeConverter.ConvertBack(DateTime.Now);
}
- if (id == 0 && Account?.Id != null)
- {
- transaction.FromAccountId = (int)Account.Id;
- }
- var transactionDto = new TransactionDto(transaction, subTransactions);
+ return (transaction, subTransactions);
+ }
- // if transaction not in home currency, replace FromAmount with OriginalFromAmount to show correct values
- if (transactionDto.IsOriginalFromAmountVisible)
- {
- foreach (var item in transactionDto.SubTransactions.OfType())
- {
- item.FromAmount = item.OriginalFromAmount ?? 0;
- }
- }
+ private async Task OpenTransactionDialogAsync(Transaction transaction, IEnumerable subTransactions)
+ {
+ var transactionDto = new TransactionDto(transaction, subTransactions);
TransactionControlVM dialogVm = new TransactionControlVM(transactionDto, dialogWrapper);
@@ -279,78 +287,99 @@ private async Task OpenTransactionDialogAsync(int id, bool isDuplicate)
var resultVm = result as TransactionDto;
if (resultVm != null)
{
- var resultTransactions = new List();
+ await SaveTransactionResult(transaction, subTransactions, resultVm);
+ }
+ }
- MapperHelper.MapTransaction(resultVm, transaction);
- long totalFromAmountHomeCurrency = transaction.FromAmount;
- resultTransactions.Add(transaction);
- if (resultVm?.SubTransactions?.Any() == true)
- {
- // TODO : Add Unit Test for code below
- foreach (var subTransactionDto in resultVm.SubTransactions.OfType())
- {
- var subTransaction = await db.GetOrCreateAsync(subTransactionDto.Id);
- subTransactionDto.Date = resultVm.Date;
- subTransactionDto.Time = resultVm.Time;
- MapperHelper.MapTransaction(subTransactionDto, subTransaction);
- subTransaction.Parent = transaction;
- subTransaction.FromAccountId = transaction.FromAccountId;
- subTransaction.OriginalCurrencyId = transaction.OriginalCurrencyId ?? transaction.FromAccount.CurrencyId;
- subTransaction.Category = default;
-
- //Set FromAmount in home currency
- if (resultVm.IsOriginalFromAmountVisible)
- {
- var originalFromAmount = (subTransactionDto).RealFromAmount;
- subTransaction.FromAmount = (long)(originalFromAmount * resultVm.Rate);
- subTransaction.OriginalFromAmount = originalFromAmount;
- totalFromAmountHomeCurrency -= subTransaction.FromAmount;
- }
+ private async Task SaveTransactionResult(Transaction transaction, IEnumerable subTransactions, TransactionDto resultVm)
+ {
+ var resultTransactions = new List();
- resultTransactions.Add(subTransaction);
- }
+ MapperHelper.MapTransaction(resultVm, transaction);
+ long totalFromAmountHomeCurrency = transaction.FromAmount;
+ resultTransactions.Add(transaction);
+ if (resultVm.SubTransactions?.Any() == true)
+ {
+ // TODO : Add Unit Test for code below
+ foreach (var subTransactionDto in resultVm.SubTransactions.OfType())
+ {
+ Transaction subTransaction = await GetSubTransaction(transaction, resultVm, subTransactionDto);
- if (!resultVm.IsOriginalFromAmountVisible)
+ //Set FromAmount in home currency
+ if (resultVm.IsOriginalFromAmountVisible)
{
- foreach (var subTranfer in resultVm.SubTransactions.OfType())
- {
- var subTransaction = await db.GetOrCreateAsync(subTranfer.Id);
-
- subTranfer.Date = resultVm.Date;
- subTranfer.Time = resultVm.Time;
- MapperHelper.MapTransfer(subTranfer, subTransaction);
- subTransaction.Parent = transaction;
- subTransaction.FromAccountId = transaction.FromAccountId;
-
- resultTransactions.Add(subTransaction);
- }
+ var originalFromAmount = (subTransactionDto).RealFromAmount;
+ subTransaction.FromAmount = (long)(originalFromAmount * resultVm.Rate);
+ subTransaction.OriginalFromAmount = originalFromAmount;
+ totalFromAmountHomeCurrency -= subTransaction.FromAmount;
}
- // check if sum of all subtransaction == parentTransaction.FromAmount
- // if not - add diference to last transaction
- if (resultVm.IsOriginalFromAmountVisible && totalFromAmountHomeCurrency != 0)
- {
- resultTransactions.Last().FromAmount += totalFromAmountHomeCurrency;
- }
+ resultTransactions.Add(subTransaction);
}
- await db.InsertOrUpdateAsync(resultTransactions);
-
- var transactionsIds = resultTransactions.Select(x => x.Id).Distinct().ToList();
- var deletedSubTransaction = subTransactions.Select(x => x.Id).Where(x => !transactionsIds.Contains(x));
- foreach (var t in deletedSubTransaction)
+ if (!resultVm.IsOriginalFromAmountVisible)
{
- await DeleteTransaction(t);
+ foreach (var subTranfer in resultVm.SubTransactions.OfType())
+ {
+ Transaction subTransaction = await GetSubTransfer(transaction, resultVm, subTranfer);
+ resultTransactions.Add(subTransaction);
+ }
}
- await db.RebuildAccountBalanceAsync(transaction.FromAccountId);
- var toAccounts = resultTransactions.Select(x => x.ToAccountId).Where(x => x > 0).Distinct().ToList();
- foreach (var account in toAccounts)
+ // check if sum of all subtransaction == parentTransaction.FromAmount
+ // if not - add diference to last transaction
+ if (resultVm.IsOriginalFromAmountVisible && totalFromAmountHomeCurrency != 0)
{
- await db.RebuildAccountBalanceAsync(account);
+ resultTransactions.Last().FromAmount += totalFromAmountHomeCurrency;
}
- await RefreshData();
}
+
+ await db.InsertOrUpdateAsync(resultTransactions);
+
+ await ProcessDeletedTransactions(subTransactions, resultTransactions);
+
+ await db.RebuildAccountBalanceAsync(transaction.FromAccountId);
+ var toAccounts = resultTransactions.Select(x => x.ToAccountId).Where(x => x > 0).Distinct().ToList();
+ foreach (var account in toAccounts)
+ {
+ await db.RebuildAccountBalanceAsync(account);
+ }
+ await RefreshData();
+ }
+
+ private async Task ProcessDeletedTransactions(IEnumerable subTransactions, List resultTransactions)
+ {
+ var transactionsIds = resultTransactions.Select(x => x.Id).Distinct().ToList();
+ var deletedSubTransaction = subTransactions.Select(x => x.Id).Where(x => !transactionsIds.Contains(x));
+ foreach (var t in deletedSubTransaction)
+ {
+ await DeleteTransaction(t);
+ }
+ }
+
+ private async Task GetSubTransfer(Transaction transaction, TransactionDto resultVm, TransferDto subTranfer)
+ {
+ var subTransaction = await db.GetOrCreateAsync(subTranfer.Id);
+
+ subTranfer.Date = resultVm.Date;
+ subTranfer.Time = resultVm.Time;
+ MapperHelper.MapTransfer(subTranfer, subTransaction);
+ subTransaction.Parent = transaction;
+ subTransaction.FromAccountId = transaction.FromAccountId;
+ return subTransaction;
+ }
+
+ private async Task GetSubTransaction(Transaction transaction, TransactionDto resultVm, TransactionDto subTransactionDto)
+ {
+ var subTransaction = await db.GetOrCreateAsync(subTransactionDto.Id);
+ subTransactionDto.Date = resultVm.Date;
+ subTransactionDto.Time = resultVm.Time;
+ MapperHelper.MapTransaction(subTransactionDto, subTransaction);
+ subTransaction.Parent = transaction;
+ subTransaction.FromAccountId = transaction.FromAccountId;
+ subTransaction.OriginalCurrencyId = transaction.OriginalCurrencyId ?? transaction.FromAccount.CurrencyId;
+ subTransaction.Category = default;
+ return subTransaction;
}
protected override async Task RefreshData()
@@ -387,7 +416,7 @@ protected override async Task RefreshData()
predicate = predicate.And(x => x.LocationId == _location.Id);
}
- var items = await repo.FindManyAsync(
+ var items = await repo.FindManyAndProjectAsync(
predicate: predicate,
projection: x => new BlotterModel
{
@@ -429,7 +458,9 @@ protected override async Task RefreshData()
Symbol = x.OriginalCurrency.Symbol,
}
},
- includes: new Expression>[] { x => x.FromAccountCurrency, x => x.ToAccountCurrency, x => x.OriginalCurrency });
+ x => x.FromAccountCurrency,
+ x => x.ToAccountCurrency,
+ x => x.OriginalCurrency);
if (items != null)
{
diff --git a/src/Financier.Desktop/Pages/CategoriesVM.cs b/src/Financier.Desktop/Pages/CategoriesVM.cs
index 2a24f455..bfe4b8dd 100644
--- a/src/Financier.Desktop/Pages/CategoriesVM.cs
+++ b/src/Financier.Desktop/Pages/CategoriesVM.cs
@@ -37,7 +37,7 @@ private void InitializeNodes(List nodes, List
{
foreach (var category in categories.OrderBy(x => x.Left))
{
- if (!nodes.Any(x => x.Right > category.Left))
+ if (!nodes.Exists(x => x.Right > category.Left))
{
var subNode = new CategoryTreeModel
{
@@ -49,7 +49,7 @@ private void InitializeNodes(List nodes, List
nodes.Add(subNode);
var sub = categories.Where(x => x.Left > category.Left && x.Right < category.Right).ToList();
- if (sub.Any())
+ if (sub.Count > 0)
{
InitializeNodes(subNode.SubCategoties, sub, level + 1);
}
diff --git a/src/Financier.Desktop/Pages/Dialogs/TransactionControlVM.cs b/src/Financier.Desktop/Pages/Dialogs/TransactionControlVM.cs
index d389eb1e..97f26f81 100644
--- a/src/Financier.Desktop/Pages/Dialogs/TransactionControlVM.cs
+++ b/src/Financier.Desktop/Pages/Dialogs/TransactionControlVM.cs
@@ -51,7 +51,7 @@ public TransactionControlVM(
private static void CopySubTransaction(TransactionDto original, TransactionDto modifiedCopy)
{
original.CategoryId = modifiedCopy.CategoryId;
- original.Category = DbManual.Category?.FirstOrDefault(x => x.Id == modifiedCopy.CategoryId);
+ original.Category = DbManual.Category?.Find(x => x.Id == modifiedCopy.CategoryId);
original.FromAmount = modifiedCopy.RealFromAmount;
original.IsAmountNegative = modifiedCopy.IsAmountNegative;
original.Note = modifiedCopy.Note;
@@ -83,7 +83,7 @@ private void ShowRecepiesDialog()
{
foreach (var item in outputTransactions)
{
- item.Category = DbManual.Category?.FirstOrDefault(x => x.Id == item.CategoryId);
+ item.Category = DbManual.Category?.Find(x => x.Id == item.CategoryId);
Transaction.SubTransactions.Add(item);
}
Transaction.RecalculateUnSplitAmount();
diff --git a/src/Financier.Desktop/Pages/ExchangeRatesVM.cs b/src/Financier.Desktop/Pages/ExchangeRatesVM.cs
index 26d7f006..db4b4662 100644
--- a/src/Financier.Desktop/Pages/ExchangeRatesVM.cs
+++ b/src/Financier.Desktop/Pages/ExchangeRatesVM.cs
@@ -65,7 +65,7 @@ protected override async Task RefreshData()
{
using var uow = db.CreateUnitOfWork();
var accountRepo = uow.GetRepository();
- var items = await accountRepo.FindManyAsync(
+ var items = await accountRepo.FindManyAndProjectAsync(
x => x.FromCurrencyId == (_from != null ? _from.Id : 0) && x.ToCurrencyId == (_to != null ? _to.Id : 0), // where
rate => new ExchangeRateModel
{
diff --git a/src/Financier.Desktop/Properties/GlobalAssemblyInfo.cs b/src/Financier.Desktop/Properties/GlobalAssemblyInfo.cs
index 05bddc9c..4112f6bd 100644
--- a/src/Financier.Desktop/Properties/GlobalAssemblyInfo.cs
+++ b/src/Financier.Desktop/Properties/GlobalAssemblyInfo.cs
@@ -1,9 +1,9 @@
-//2023.11.28.1
+//2024.1.18.4
using System.Reflection;
[assembly: AssemblyCompany("Financier.Desktop")]
[assembly: AssemblyProduct("Financier.Desktop")]
[assembly: AssemblyTitle("Financier.Desktop")]
[assembly: AssemblyConfiguration("Release")]
-[assembly: AssemblyFileVersion("2023.11.28.1")]
-[assembly: AssemblyVersion("2023.11.28.1")]
+[assembly: AssemblyFileVersion("2024.1.18.4")]
+[assembly: AssemblyVersion("2024.1.18.4")]
diff --git a/src/Financier.Desktop/Properties/PublishProfiles/FolderProfile.pubxml b/src/Financier.Desktop/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 00000000..9c89fbed
--- /dev/null
+++ b/src/Financier.Desktop/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,18 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\publish\win-x64\
+ FileSystem
+ <_TargetId>Folder
+ net8.0-windows7.0
+ win-x64
+ false
+ true
+ true
+
+
\ No newline at end of file
diff --git a/src/Financier.Desktop/Properties/Settings.Designer.cs b/src/Financier.Desktop/Properties/Settings.Designer.cs
index 64e83e63..b1e66630 100644
--- a/src/Financier.Desktop/Properties/Settings.Designer.cs
+++ b/src/Financier.Desktop/Properties/Settings.Designer.cs
@@ -1,26 +1,22 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
+using System.Configuration;
+
+namespace Financier.Desktop.Properties
+{
-namespace Financier.Desktop.Properties {
-
-
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")]
- internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
-
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-
- public static Settings Default {
- get {
- return defaultInstance;
- }
+
+ public static Settings Default { get { return defaultInstance; } }
+
+ [UserScopedSetting]
+ public string DefaultBackupDir
+ {
+ get => (string)this[nameof(DefaultBackupDir)];
+ set => this[nameof(DefaultBackupDir)] = (object)value;
}
}
}
diff --git a/src/Financier.Desktop/Wizards/FinancierTransactionDTO.cs b/src/Financier.Desktop/Wizards/FinancierTransactionDTO.cs
index 5f47418e..88d0060f 100644
--- a/src/Financier.Desktop/Wizards/FinancierTransactionDTO.cs
+++ b/src/Financier.Desktop/Wizards/FinancierTransactionDTO.cs
@@ -9,6 +9,7 @@ public class FinancierTransactionDto : BindableBase
private string note;
private int order;
private int projectId;
+ private int payeeId;
private int toAccountId;
public int CategoryId
@@ -73,6 +74,15 @@ public int ProjectId
RaisePropertyChanged(nameof(ProjectId));
}
}
+ public int PayeeId
+ {
+ get => payeeId;
+ set
+ {
+ payeeId = value;
+ RaisePropertyChanged(nameof(PayeeId));
+ }
+ }
public int ToAccountId
{
diff --git a/src/Financier.Desktop/Wizards/MonoWizard/MonoWizardVM.cs b/src/Financier.Desktop/Wizards/MonoWizard/MonoWizardVM.cs
index 9f299d43..9fa5244d 100644
--- a/src/Financier.Desktop/Wizards/MonoWizard/MonoWizardVM.cs
+++ b/src/Financier.Desktop/Wizards/MonoWizard/MonoWizardVM.cs
@@ -86,6 +86,7 @@ private Transaction TransformMonoTransaction(FinancierTransactionDto x)
Note = x.Note,
LocationId = x.LocationId,
ProjectId = x.ProjectId,
+ PayeeId = x.PayeeId,
CategoryId = 0,
Category = default,
DateTime = x.DateTime,
diff --git a/src/Financier.Desktop/Wizards/MonoWizard/Page2VM.cs b/src/Financier.Desktop/Wizards/MonoWizard/Page2VM.cs
index 4167c279..d6732536 100644
--- a/src/Financier.Desktop/Wizards/MonoWizard/Page2VM.cs
+++ b/src/Financier.Desktop/Wizards/MonoWizard/Page2VM.cs
@@ -57,8 +57,8 @@ public AccountFilterModel MonoAccount
public List GetMonoTransactions()
{
- var startDate = _startTransaction?.Date ?? new DateTime(2017, 11, 17); // Monobank launched
- return allTransactions.OrderByDescending(x => x.Date).Where(x => x.Date > startDate).ToList();
+ var startDate = _startTransaction?.Date ?? new DateTime(2017, 11, 17, 0, 0, 0, DateTimeKind.Local); // Monobank launched
+ return allTransactions.Where(x => x.Date > startDate).OrderByDescending(x => x.Date).ToList();
}
public BankTransaction StartTransaction
diff --git a/src/Financier.Desktop/Wizards/MonoWizard/Page3.xaml b/src/Financier.Desktop/Wizards/MonoWizard/Page3.xaml
index fa46425c..fdc3e2c0 100644
--- a/src/Financier.Desktop/Wizards/MonoWizard/Page3.xaml
+++ b/src/Financier.Desktop/Wizards/MonoWizard/Page3.xaml
@@ -238,6 +238,22 @@
+
+
+
+
+
+
+
diff --git a/src/Financier.Desktop/Wizards/WizardWindow.xaml b/src/Financier.Desktop/Wizards/WizardWindow.xaml
index d3952b62..08cec8c7 100644
--- a/src/Financier.Desktop/Wizards/WizardWindow.xaml
+++ b/src/Financier.Desktop/Wizards/WizardWindow.xaml
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="{Binding Title}"
- Width="1040"
+ Width="1100"
Height="620"
Background="{DynamicResource WindowBackgroundBrush}"
UseLayoutRounding="True"
@@ -31,8 +31,9 @@
-
+
- net6.0-windows
+ net8.0-windows7.0
enable
true
diff --git a/src/Financier.Reports/Generic.xaml b/src/Financier.Reports/Generic.xaml
index c4b7369c..384d8f6e 100644
--- a/src/Financier.Reports/Generic.xaml
+++ b/src/Financier.Reports/Generic.xaml
@@ -17,8 +17,8 @@