Add new blazor app
This commit is contained in:
parent
4c598f0827
commit
ad75c47709
155 changed files with 64093 additions and 1 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -51,4 +51,8 @@ CodeCoverage/
|
||||||
# NUnit
|
# NUnit
|
||||||
*.VisualState.xml
|
*.VisualState.xml
|
||||||
TestResult.xml
|
TestResult.xml
|
||||||
nunit-*.xml
|
nunit-*.xml
|
||||||
|
/AccountingWeb/.vs
|
||||||
|
/.vs/Accounting
|
||||||
|
/.vs/ProjectEvaluation
|
||||||
|
/Schaad.Accounting.UI/.vs
|
||||||
|
|
|
||||||
13
.idea/.idea.Accounting/.idea/.gitignore
vendored
Normal file
13
.idea/.idea.Accounting/.idea/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/modules.xml
|
||||||
|
/contentModel.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/.idea.Accounting.iml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
1
.idea/.idea.Accounting/.idea/.name
Normal file
1
.idea/.idea.Accounting/.idea/.name
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Accounting
|
||||||
8
.idea/.idea.Accounting/.idea/dictionaries/project.xml
Normal file
8
.idea/.idea.Accounting/.idea/dictionaries/project.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="project">
|
||||||
|
<words>
|
||||||
|
<w>schaad</w>
|
||||||
|
<w>spendings</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
4
.idea/.idea.Accounting/.idea/encodings.xml
Normal file
4
.idea/.idea.Accounting/.idea/encodings.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
8
.idea/.idea.Accounting/.idea/indexLayout.xml
Normal file
8
.idea/.idea.Accounting/.idea/indexLayout.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/.idea.Accounting/.idea/vcs.xml
Normal file
6
.idea/.idea.Accounting/.idea/vcs.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
43
Accounting.sln
Normal file
43
Accounting.sln
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.13.35931.197
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schaad.Accounting.UI", "Schaad.Accounting.UI\Schaad.Accounting.UI.csproj", "{18BB46CF-FD35-6A8A-C11E-7F5405927F50}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schaad.Accounting.Common", "Schaad.Accounting.Common\Schaad.Accounting.Common.csproj", "{ACC60040-DC4A-6690-7600-44585B2B5508}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schaad.Accounting.Services", "Schaad.Accounting.Services\Schaad.Accounting.Services.csproj", "{0F1A55C2-5D6B-601E-2BB2-0725B5E4C7DA}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schaad.Accounting.Db", "Schaad.Accounting.Db\Schaad.Accounting.Db.csproj", "{DC3EEA5C-7075-5F02-00A2-95BCDD87BB59}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{18BB46CF-FD35-6A8A-C11E-7F5405927F50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{18BB46CF-FD35-6A8A-C11E-7F5405927F50}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{18BB46CF-FD35-6A8A-C11E-7F5405927F50}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{18BB46CF-FD35-6A8A-C11E-7F5405927F50}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{ACC60040-DC4A-6690-7600-44585B2B5508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{ACC60040-DC4A-6690-7600-44585B2B5508}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{ACC60040-DC4A-6690-7600-44585B2B5508}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{ACC60040-DC4A-6690-7600-44585B2B5508}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0F1A55C2-5D6B-601E-2BB2-0725B5E4C7DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0F1A55C2-5D6B-601E-2BB2-0725B5E4C7DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0F1A55C2-5D6B-601E-2BB2-0725B5E4C7DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0F1A55C2-5D6B-601E-2BB2-0725B5E4C7DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DC3EEA5C-7075-5F02-00A2-95BCDD87BB59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DC3EEA5C-7075-5F02-00A2-95BCDD87BB59}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DC3EEA5C-7075-5F02-00A2-95BCDD87BB59}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DC3EEA5C-7075-5F02-00A2-95BCDD87BB59}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {9C94ED36-E7EA-455C-967A-C293DD9B4C81}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
4
Accounting.sln.DotSettings.user
Normal file
4
Accounting.sln.DotSettings.user
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABar_002Ecs_002Fl_003AC_0021_003FUsers_003Fschaa_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F3d754f61d7ca16fefcc624d522ddfb3235d54b85ab5638c1e181cde5af4357d_003FBar_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003AC_0021_003FUsers_003Fschaa_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fbd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003AC_0021_003FUsers_003Fschaa_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F6354a7b35d7821629924d3676acd7e67a6f7f94343e0e66ec439aa2bd6ed5_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||||
10
Schaad.Accounting.Common/ClassIds.cs
Normal file
10
Schaad.Accounting.Common/ClassIds.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Schaad.Accounting
|
||||||
|
{
|
||||||
|
public class ClassIds
|
||||||
|
{
|
||||||
|
public const int Activa = 1;
|
||||||
|
public const int Passiva = 2;
|
||||||
|
public const int Income = 3;
|
||||||
|
public const int Expenses = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Schaad.Accounting.Common/Datasets/AccountDataset.cs
Normal file
38
Schaad.Accounting.Common/Datasets/AccountDataset.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets
|
||||||
|
{
|
||||||
|
public class AccountDataset : Account
|
||||||
|
{
|
||||||
|
public decimal Balance { get; }
|
||||||
|
|
||||||
|
public decimal BalanceCHF { get; }
|
||||||
|
|
||||||
|
public decimal StartBalanceCHF { get; }
|
||||||
|
|
||||||
|
public string ClassName { get; }
|
||||||
|
|
||||||
|
public string SubClassName { get; }
|
||||||
|
|
||||||
|
public AccountDataset(Account account, decimal balance, decimal balanceCHF, decimal startBalanceCHF, string className, string subClassName)
|
||||||
|
{
|
||||||
|
LastBankBalance = account.LastBankBalance;
|
||||||
|
StartBalance = account.StartBalance;
|
||||||
|
StartBalanceCHF = startBalanceCHF;
|
||||||
|
BankAccountNumber = account.BankAccountNumber;
|
||||||
|
Currency = account.Currency;
|
||||||
|
Id = account.Id;
|
||||||
|
Name = account.Name;
|
||||||
|
Number = account.Number;
|
||||||
|
Balance = balance;
|
||||||
|
BalanceCHF = balanceCHF;
|
||||||
|
ClassName = className;
|
||||||
|
SubClassName = subClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("Name: {0}, Number: {1}, Balance {2}", Name, Number, Balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Schaad.Accounting.Common/Datasets/BankTransactionDataset.cs
Normal file
15
Schaad.Accounting.Common/Datasets/BankTransactionDataset.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets
|
||||||
|
{
|
||||||
|
public class BankTransactionDataset
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public DateTime BookingDate { get; set; }
|
||||||
|
public DateTime ValueDate { get; set; }
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
public string Debtor { get; set; }
|
||||||
|
public string Creditor { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Schaad.Accounting.Common/Datasets/BookingRuleDataset.cs
Normal file
19
Schaad.Accounting.Common/Datasets/BookingRuleDataset.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets
|
||||||
|
{
|
||||||
|
public class BookingRuleDataset : BookingRule
|
||||||
|
{
|
||||||
|
public string Account { get; set; }
|
||||||
|
|
||||||
|
public BookingRuleDataset(BookingRule bookingRule, string account)
|
||||||
|
{
|
||||||
|
this.LookupText = bookingRule.LookupText;
|
||||||
|
this.LookupValue = bookingRule.LookupValue;
|
||||||
|
this.BookingText = bookingRule.BookingText;
|
||||||
|
this.Id = bookingRule.Id;
|
||||||
|
this.AccountId = bookingRule.AccountId;
|
||||||
|
this.Account = account;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Schaad.Accounting.Common/Datasets/Charts/DataSerie.cs
Normal file
7
Schaad.Accounting.Common/Datasets/Charts/DataSerie.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets.Charts
|
||||||
|
{
|
||||||
|
public record DataSerie(string Id, string Name, IReadOnlyList<DateOnly> X, IReadOnlyList<decimal> Y);
|
||||||
|
}
|
||||||
49
Schaad.Accounting.Common/Datasets/MessageDataset.cs
Normal file
49
Schaad.Accounting.Common/Datasets/MessageDataset.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets
|
||||||
|
{
|
||||||
|
public class MessageDataset
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
public List<string> Lines { get; set; }
|
||||||
|
|
||||||
|
public MessageStatus Status { get; set; }
|
||||||
|
|
||||||
|
public MessageDataset(string title, MessageStatus status = MessageStatus.Success)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
Status = status;
|
||||||
|
Lines = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(string message, MessageStatus status = MessageStatus.Success)
|
||||||
|
{
|
||||||
|
Lines.Add(message);
|
||||||
|
if (status > Status)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCssAlertClass()
|
||||||
|
{
|
||||||
|
switch (Status)
|
||||||
|
{
|
||||||
|
case MessageStatus.Success: return "alert-success";
|
||||||
|
case MessageStatus.Info: return "alert-info";
|
||||||
|
case MessageStatus.Warning: return "alert-warning";
|
||||||
|
case MessageStatus.Error: return "alert-danger";
|
||||||
|
default: return "alert-success";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MessageStatus
|
||||||
|
{
|
||||||
|
Success = 0,
|
||||||
|
Info = 1,
|
||||||
|
Warning = 2,
|
||||||
|
Error = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Schaad.Accounting.Common/Datasets/Reports/BalanceDataset.cs
Normal file
23
Schaad.Accounting.Common/Datasets/Reports/BalanceDataset.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets.Reports
|
||||||
|
{
|
||||||
|
public class BalanceDataset
|
||||||
|
{
|
||||||
|
public List<AccountDataset> ActivaAccountList { get; set; }
|
||||||
|
|
||||||
|
public List<AccountDataset> PassivaAccountList { get; set; }
|
||||||
|
|
||||||
|
public decimal TotalActivaCHF { get; set; }
|
||||||
|
|
||||||
|
public decimal TotalPassivaCHF { get; set; }
|
||||||
|
|
||||||
|
public BalanceDataset(List<AccountDataset> activaAccountList, List<AccountDataset> passivaAccountList, decimal totalActivaCHF, decimal totalPassivaCHF)
|
||||||
|
{
|
||||||
|
ActivaAccountList = activaAccountList;
|
||||||
|
PassivaAccountList = passivaAccountList;
|
||||||
|
TotalActivaCHF = totalActivaCHF;
|
||||||
|
TotalPassivaCHF = totalPassivaCHF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets.Reports
|
||||||
|
{
|
||||||
|
public class BalanceSheetDataset
|
||||||
|
{
|
||||||
|
public List<AccountDataset> ActivaAccountList { get; }
|
||||||
|
|
||||||
|
public List<AccountDataset> IncomeAccountList { get; }
|
||||||
|
|
||||||
|
public List<AccountDataset> ExpensesAccountList { get; }
|
||||||
|
|
||||||
|
public decimal ProfitCHF { get; }
|
||||||
|
|
||||||
|
public decimal LossCHF { get; }
|
||||||
|
|
||||||
|
public int Year { get; }
|
||||||
|
|
||||||
|
public BalanceSheetDataset(List<AccountDataset> activaAccountList, List<AccountDataset> incomeAccountList, List<AccountDataset> expensesAccountList, decimal profitCHF, decimal lossCHF, int year)
|
||||||
|
{
|
||||||
|
ActivaAccountList = activaAccountList;
|
||||||
|
IncomeAccountList = incomeAccountList;
|
||||||
|
ExpensesAccountList = expensesAccountList;
|
||||||
|
ProfitCHF = profitCHF;
|
||||||
|
LossCHF = lossCHF;
|
||||||
|
Year = year;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Schaad.Accounting.Common/Datasets/SettingsDataset.cs
Normal file
9
Schaad.Accounting.Common/Datasets/SettingsDataset.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Schaad.Accounting.Datasets
|
||||||
|
{
|
||||||
|
public class SettingsDataset
|
||||||
|
{
|
||||||
|
public string DataPath { get; set; }
|
||||||
|
|
||||||
|
public string FixerIoApiKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Schaad.Accounting.Common/Datasets/TransactionDataset.cs
Normal file
37
Schaad.Accounting.Common/Datasets/TransactionDataset.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Datasets
|
||||||
|
{
|
||||||
|
public class TransactionDataset : Transaction
|
||||||
|
{
|
||||||
|
public Account OriginAccount { get; set; }
|
||||||
|
|
||||||
|
public Account TargetAccount { get; set; }
|
||||||
|
|
||||||
|
public TransactionDataset(Transaction transaction, Account originAccount, Account targetAccout)
|
||||||
|
{
|
||||||
|
this.BankTransactionId = transaction.BankTransactionId;
|
||||||
|
this.Id = transaction.Id;
|
||||||
|
this.OriginAccountId = transaction.OriginAccountId;
|
||||||
|
this.TargetAccountId = transaction.TargetAccountId;
|
||||||
|
this.Text = transaction.Text;
|
||||||
|
this.Value = transaction.Value;
|
||||||
|
this.FxRate = transaction.FxRate;
|
||||||
|
this.ValueDate = transaction.ValueDate;
|
||||||
|
this.BookingDate = transaction.BookingDate;
|
||||||
|
this.OriginAccount = originAccount;
|
||||||
|
this.TargetAccount = targetAccout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public decimal GetValue(bool withFxRate)
|
||||||
|
{
|
||||||
|
if (withFxRate && FxRate != 0)
|
||||||
|
{
|
||||||
|
return Math.Round(Value / FxRate.Value, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Common.Extensions
|
||||||
|
{
|
||||||
|
public static class DecimalExtension
|
||||||
|
{
|
||||||
|
public static string ToFormattedString(this decimal value)
|
||||||
|
{
|
||||||
|
var culture = new CultureInfo("de-CH");
|
||||||
|
culture.NumberFormat.NumberGroupSeparator = "'";
|
||||||
|
return value.ToString("#,0.00", culture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Schaad.Accounting.Common/Interfaces/IAccountRepository.cs
Normal file
20
Schaad.Accounting.Common/Interfaces/IAccountRepository.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface IAccountRepository
|
||||||
|
{
|
||||||
|
List<Account> GetAccountList();
|
||||||
|
|
||||||
|
void SaveAccount(Account account);
|
||||||
|
|
||||||
|
Account GetAccount(string id);
|
||||||
|
|
||||||
|
void DeleteAccount(string id);
|
||||||
|
|
||||||
|
Account GetAccountForBankAccountNumber(string bankAccountNumber);
|
||||||
|
|
||||||
|
void SaveBankAccountBalance(string bankAccountNumber, decimal accountBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface IBankTransactionRepository
|
||||||
|
{
|
||||||
|
List<BankTransaction> GetBankTransactionList();
|
||||||
|
|
||||||
|
int SaveBankTransactionList(string bankAccountNumber, List<BankTransactionDataset> bankTransactions);
|
||||||
|
|
||||||
|
BankTransaction GetBankTransaction(string id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface IBookingRuleRepository
|
||||||
|
{
|
||||||
|
List<BookingRule> GetBookingRuleList();
|
||||||
|
|
||||||
|
void SaveBookingRule(BookingRule bookingRule);
|
||||||
|
|
||||||
|
BookingRule GetBookingRule(string id);
|
||||||
|
void DeleteBookingRule(string id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface IBookingTextRepository
|
||||||
|
{
|
||||||
|
List<BookingText> GetBookingTextList();
|
||||||
|
|
||||||
|
void SaveBookingText(BookingText bookingText);
|
||||||
|
|
||||||
|
BookingText GetBookingText(string id);
|
||||||
|
|
||||||
|
void DeleteBookingText(string id);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Schaad.Accounting.Common/Interfaces/IChartService.cs
Normal file
10
Schaad.Accounting.Common/Interfaces/IChartService.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Datasets.Charts;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface IChartService
|
||||||
|
{
|
||||||
|
IReadOnlyList<DataSerie> GetExpensesPerMonth();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Schaad.Accounting.Common/Interfaces/IFileService.cs
Normal file
15
Schaad.Accounting.Common/Interfaces/IFileService.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Finance.Api;
|
||||||
|
using Schaad.Finance.Api.Datasets;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface IFileService
|
||||||
|
{
|
||||||
|
string Backup();
|
||||||
|
IReadOnlyList<MessageDataset> ImportAccountStatementFile(string filePath);
|
||||||
|
IReadOnlyList<CreditCardTransaction> ImportCreditCardStatementFile(CreditCardProvider creditCardProvider, string filePath);
|
||||||
|
byte[] GetTransactionListCsv(string accountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Schaad.Accounting.Common/Interfaces/ISettingsService.cs
Normal file
33
Schaad.Accounting.Common/Interfaces/ISettingsService.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface ISettingsService
|
||||||
|
{
|
||||||
|
int GetYear();
|
||||||
|
|
||||||
|
void SetYear(int year);
|
||||||
|
|
||||||
|
bool TrySetYear(int year);
|
||||||
|
|
||||||
|
string GetMandator();
|
||||||
|
|
||||||
|
void SetMandator(string mandator);
|
||||||
|
|
||||||
|
string GetYearMandatorPath();
|
||||||
|
|
||||||
|
string GetLastYearMandatorPath();
|
||||||
|
|
||||||
|
string GetBackupFilePath();
|
||||||
|
|
||||||
|
string GetUploadPath();
|
||||||
|
|
||||||
|
string GetCreditCardStatementPath();
|
||||||
|
|
||||||
|
string GetDbPath();
|
||||||
|
|
||||||
|
string GetLastYearDbPath();
|
||||||
|
|
||||||
|
SettingsDataset GetSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface ISplitPredefinitonRepository
|
||||||
|
{
|
||||||
|
List<SplitPredefinition> GetSplitPredefinitionList();
|
||||||
|
|
||||||
|
void SaveSplitPredefinition(SplitPredefinition splitPredefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Schaad.Accounting.Common/Interfaces/ISubclassRepository.cs
Normal file
20
Schaad.Accounting.Common/Interfaces/ISubclassRepository.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface ISubclassRepository
|
||||||
|
{
|
||||||
|
List<SubClass> GetSubClassList();
|
||||||
|
|
||||||
|
void SaveSubClass(SubClass subClass);
|
||||||
|
|
||||||
|
SubClass GetSubClass(string id);
|
||||||
|
|
||||||
|
void DeleteSubClass(string id);
|
||||||
|
|
||||||
|
Dictionary<int, string> GetClasses();
|
||||||
|
|
||||||
|
string GetClass(int classId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface ITransactionRepository
|
||||||
|
{
|
||||||
|
List<Transaction> GetTransactionList();
|
||||||
|
|
||||||
|
void SaveTransaction(Transaction transaction);
|
||||||
|
|
||||||
|
Transaction GetTransaction(string id);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Schaad.Accounting.Common/Interfaces/IViewService.cs
Normal file
31
Schaad.Accounting.Common/Interfaces/IViewService.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Datasets.Reports;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
using Schaad.Finance.Api.Datasets;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Interfaces
|
||||||
|
{
|
||||||
|
public interface IViewService
|
||||||
|
{
|
||||||
|
AccountDataset GetAccountView(string id);
|
||||||
|
|
||||||
|
List<AccountDataset> GetAccountViewList();
|
||||||
|
|
||||||
|
BalanceDataset GetBalanceView();
|
||||||
|
|
||||||
|
BalanceSheetDataset GetBalanceSheetView(int year);
|
||||||
|
|
||||||
|
List<TransactionDataset> GetTransactionViewList();
|
||||||
|
|
||||||
|
List<TransactionDataset> GetTransactionViewList(string accountId);
|
||||||
|
|
||||||
|
List<BookingRuleDataset> GetBookingRuleViewList();
|
||||||
|
|
||||||
|
List<BankTransaction> GetOpenBankTransactionList();
|
||||||
|
|
||||||
|
List<Transaction> MatchOpenBankTransactions();
|
||||||
|
|
||||||
|
List<Transaction> MatchCreditCardTransactions(string bankTransactionId, IReadOnlyList<CreditCardTransaction> creditCardTransactions);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
Schaad.Accounting.Common/Models/Account.cs
Normal file
77
Schaad.Accounting.Common/Models/Account.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Models
|
||||||
|
{
|
||||||
|
public class Account
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Nummer")]
|
||||||
|
[Required]
|
||||||
|
public int Number { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Name")]
|
||||||
|
[Required]
|
||||||
|
[MinLength(3)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Währung")]
|
||||||
|
[Required]
|
||||||
|
[MinLength(3)]
|
||||||
|
[MaxLength(3)]
|
||||||
|
public string Currency { get; set; }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
[Display(Name = "Klasse")]
|
||||||
|
public int Class
|
||||||
|
{
|
||||||
|
get { return Number / 1000; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
[Display(Name = "Subklasse")]
|
||||||
|
public int SubClass
|
||||||
|
{
|
||||||
|
get { return Number / 100; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If mapped to a bank account
|
||||||
|
/// </summary>
|
||||||
|
[Display(Name = "Bankkontonummer")]
|
||||||
|
public string BankAccountNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start balance for acitvas
|
||||||
|
/// </summary>
|
||||||
|
[Display(Name = "Start Saldo")]
|
||||||
|
public decimal StartBalance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Last received balance from bank
|
||||||
|
/// </summary>
|
||||||
|
[Display(Name = "Letzter Banksaldo")]
|
||||||
|
public decimal LastBankBalance { get; set; }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsFxAccount
|
||||||
|
{
|
||||||
|
get { return string.IsNullOrEmpty(Currency) == false && Currency != "CHF"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes a copy
|
||||||
|
/// </summary>
|
||||||
|
public void Copy(Account target)
|
||||||
|
{
|
||||||
|
target.LastBankBalance = LastBankBalance;
|
||||||
|
target.StartBalance = StartBalance;
|
||||||
|
target.BankAccountNumber = BankAccountNumber;
|
||||||
|
target.Currency = string.IsNullOrEmpty(Currency) ? "CHF" : Currency;
|
||||||
|
target.Id = Id;
|
||||||
|
target.Name = Name;
|
||||||
|
target.Number = Number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Schaad.Accounting.Common/Models/BankTransaction.cs
Normal file
27
Schaad.Accounting.Common/Models/BankTransaction.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Models
|
||||||
|
{
|
||||||
|
public class BankTransaction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public DateTime ValueDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime BookingDate { get; set; }
|
||||||
|
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
|
||||||
|
public string BankAccountNumber { get; set; }
|
||||||
|
|
||||||
|
public string Debtor { get; set; }
|
||||||
|
public string Creditor { get; set; }
|
||||||
|
|
||||||
|
public bool Ignore { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Schaad.Accounting.Common/Models/BookingRule.cs
Normal file
37
Schaad.Accounting.Common/Models/BookingRule.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Models
|
||||||
|
{
|
||||||
|
public class BookingRule
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Suchtext")]
|
||||||
|
[Required]
|
||||||
|
public string LookupText { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Suchbetrag")]
|
||||||
|
public decimal LookupValue { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Buchungstext")]
|
||||||
|
[Required]
|
||||||
|
public string BookingText { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Konto")]
|
||||||
|
[Required]
|
||||||
|
public string AccountId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes a copy
|
||||||
|
/// </summary>
|
||||||
|
public void Copy(BookingRule target)
|
||||||
|
{
|
||||||
|
target.LookupText = LookupText;
|
||||||
|
target.LookupValue = LookupValue;
|
||||||
|
target.BookingText = BookingText;
|
||||||
|
target.Id = Id;
|
||||||
|
target.AccountId = AccountId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Schaad.Accounting.Common/Models/BookingText.cs
Normal file
23
Schaad.Accounting.Common/Models/BookingText.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Models
|
||||||
|
{
|
||||||
|
public class BookingText
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Buchungstext")]
|
||||||
|
[Required]
|
||||||
|
[MinLength(3)]
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes a copy
|
||||||
|
/// </summary>
|
||||||
|
public void Copy(BookingText target)
|
||||||
|
{
|
||||||
|
target.Id = Id;
|
||||||
|
target.Text = Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Schaad.Accounting.Common/Models/SplitPredefinition.cs
Normal file
25
Schaad.Accounting.Common/Models/SplitPredefinition.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
namespace Schaad.Accounting.Models
|
||||||
|
{
|
||||||
|
public class SplitPredefinition
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public string BookingText { get; set; }
|
||||||
|
|
||||||
|
public decimal BookingValue { get; set; }
|
||||||
|
|
||||||
|
public string AccountId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes a copy
|
||||||
|
/// </summary>
|
||||||
|
public void Copy(SplitPredefinition target)
|
||||||
|
{
|
||||||
|
target.BookingText = BookingText;
|
||||||
|
target.BookingValue = BookingValue;
|
||||||
|
target.Id = Id;
|
||||||
|
target.AccountId = AccountId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
Schaad.Accounting.Common/Models/SubClass.cs
Normal file
28
Schaad.Accounting.Common/Models/SubClass.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Models
|
||||||
|
{
|
||||||
|
public class SubClass
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Nummer")]
|
||||||
|
[Required]
|
||||||
|
public int Number { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Name")]
|
||||||
|
[Required]
|
||||||
|
[MinLength(3)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes a copy
|
||||||
|
/// </summary>
|
||||||
|
public void Copy(SubClass target)
|
||||||
|
{
|
||||||
|
target.Id = Id;
|
||||||
|
target.Name = Name;
|
||||||
|
target.Number = Number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
Schaad.Accounting.Common/Models/Transaction.cs
Normal file
84
Schaad.Accounting.Common/Models/Transaction.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Models
|
||||||
|
{
|
||||||
|
public class Transaction
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string BankTransactionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Needed for booking rule
|
||||||
|
/// </summary>
|
||||||
|
public string BankTransactionText { get; set; }
|
||||||
|
|
||||||
|
public string RelatedParty { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Valuta")]
|
||||||
|
public DateTime ValueDate { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Buchung")]
|
||||||
|
public DateTime BookingDate { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Buchungstext")]
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Betrag")]
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Habenkonto")]
|
||||||
|
public string OriginAccountId { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Sollkonto")]
|
||||||
|
public string TargetAccountId { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Währungskurs")]
|
||||||
|
public decimal? FxRate { get; set; }
|
||||||
|
|
||||||
|
public Transaction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transaction(BankTransaction bankTransaction, List<Account> accounts)
|
||||||
|
{
|
||||||
|
BankTransactionId = bankTransaction.Id;
|
||||||
|
BankTransactionText = bankTransaction.Text;
|
||||||
|
Value = bankTransaction.Value;
|
||||||
|
ValueDate = bankTransaction.ValueDate;
|
||||||
|
BookingDate = bankTransaction.BookingDate;
|
||||||
|
|
||||||
|
// Depending on positiv or negative value, set the account
|
||||||
|
var bankAccount = accounts.FirstOrDefault(a => a.BankAccountNumber == bankTransaction.BankAccountNumber);
|
||||||
|
if (Value >= 0)
|
||||||
|
{
|
||||||
|
TargetAccountId = bankAccount.Id;
|
||||||
|
RelatedParty = bankTransaction.Debtor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OriginAccountId = bankAccount.Id;
|
||||||
|
RelatedParty = bankTransaction.Creditor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes a copy
|
||||||
|
/// </summary>
|
||||||
|
public void Copy(Transaction target)
|
||||||
|
{
|
||||||
|
target.BankTransactionId = BankTransactionId;
|
||||||
|
target.BankTransactionText = BankTransactionText;
|
||||||
|
target.Id = Id;
|
||||||
|
target.OriginAccountId = OriginAccountId;
|
||||||
|
target.TargetAccountId = TargetAccountId;
|
||||||
|
target.Text = Text;
|
||||||
|
target.Value = Value;
|
||||||
|
target.ValueDate = ValueDate;
|
||||||
|
target.BookingDate = BookingDate > DateTime.MinValue ? BookingDate : ValueDate;
|
||||||
|
target.FxRate = FxRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Schaad.Accounting.Common/Schaad.Accounting.Common.csproj
Normal file
17
Schaad.Accounting.Common/Schaad.Accounting.Common.csproj
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<RootNamespace>Schaad.Accounting</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||||
|
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||||
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Schaad.Finance.Api">
|
||||||
|
<HintPath>..\dlls\Schaad.Finance.Api.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
114
Schaad.Accounting.Db/Repositories/AccountRepository.cs
Normal file
114
Schaad.Accounting.Db/Repositories/AccountRepository.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public class AccountRepository : BaseRepository, IAccountRepository
|
||||||
|
{
|
||||||
|
private readonly string ACCOUNTS = "Accounts.xml";
|
||||||
|
|
||||||
|
public AccountRepository(ISettingsService settingsService) : base(settingsService)
|
||||||
|
{
|
||||||
|
EnsureFileExisits(ACCOUNTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load accounts
|
||||||
|
/// </summary>
|
||||||
|
public List<Account> GetAccountList()
|
||||||
|
{
|
||||||
|
var accounts = Load<List<Account>>(ACCOUNTS);
|
||||||
|
return accounts ?? new List<Account>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save an account (insert/update)
|
||||||
|
/// </summary>
|
||||||
|
public void SaveAccount(Account account)
|
||||||
|
{
|
||||||
|
var accounts = GetAccountList();
|
||||||
|
var existingAccount = accounts.FirstOrDefault(a => a.Id == account.Id);
|
||||||
|
|
||||||
|
if (existingAccount == null)
|
||||||
|
{
|
||||||
|
existingAccount = new Account();
|
||||||
|
accounts.Add(existingAccount);
|
||||||
|
account.Id = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
account.Copy(existingAccount);
|
||||||
|
Save(accounts, ACCOUNTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get account
|
||||||
|
/// </summary>
|
||||||
|
public Account GetAccount(string id)
|
||||||
|
{
|
||||||
|
var accounts = GetAccountList();
|
||||||
|
return accounts.FirstOrDefault(t => t.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete account
|
||||||
|
/// </summary>
|
||||||
|
public void DeleteAccount(string id)
|
||||||
|
{
|
||||||
|
var accounts = GetAccountList();
|
||||||
|
accounts.RemoveAll(t => t.Id == id);
|
||||||
|
Save(accounts, ACCOUNTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get account
|
||||||
|
/// </summary>
|
||||||
|
public Account GetAccountForBankAccountNumber(string bankAccountNumber)
|
||||||
|
{
|
||||||
|
var accounts = GetAccountList();
|
||||||
|
return accounts.SingleOrDefault(t => t.BankAccountNumber == bankAccountNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveBankAccountBalance(string bankAccountNumber, decimal accountBalance)
|
||||||
|
{
|
||||||
|
var account = GetAccountForBankAccountNumber(bankAccountNumber);
|
||||||
|
account.LastBankBalance = accountBalance;
|
||||||
|
SaveAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
private new void EnsureFileExisits(string file)
|
||||||
|
{
|
||||||
|
string filePath = Path.Combine(settingsService.GetDbPath(), file);
|
||||||
|
if (File.Exists(filePath) == false)
|
||||||
|
{
|
||||||
|
var lastYearFile = Path.Combine(settingsService.GetLastYearDbPath(), file);
|
||||||
|
if (File.Exists(lastYearFile))
|
||||||
|
File.Copy(lastYearFile, filePath);
|
||||||
|
|
||||||
|
// set start balance to last bank balance
|
||||||
|
if (file.IndexOf("Accounts") > -1)
|
||||||
|
{
|
||||||
|
var accounts = GetAccountList();
|
||||||
|
foreach (var account in accounts.Where(a => a.Class == ClassIds.Activa))
|
||||||
|
{
|
||||||
|
// bank accounts
|
||||||
|
if (account.LastBankBalance > 0)
|
||||||
|
{
|
||||||
|
account.StartBalance = account.LastBankBalance;
|
||||||
|
SaveAccount(account);
|
||||||
|
}
|
||||||
|
// cash accounts
|
||||||
|
else if (account.Number < 1010)
|
||||||
|
{
|
||||||
|
account.StartBalance = 0;
|
||||||
|
SaveAccount(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public class BankTransactionRepository : BaseRepository, IBankTransactionRepository
|
||||||
|
{
|
||||||
|
private readonly string BANK_TRANSACTIONS = "BankTransactions.xml";
|
||||||
|
|
||||||
|
public BankTransactionRepository(ISettingsService settingsService) : base(settingsService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load bank transactions
|
||||||
|
/// </summary>
|
||||||
|
public List<BankTransaction> GetBankTransactionList()
|
||||||
|
{
|
||||||
|
var transactions = Load<List<BankTransaction>>(BANK_TRANSACTIONS);
|
||||||
|
return transactions != null ? transactions : new List<BankTransaction>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save new bank transactions, ignore existing ones (no update)
|
||||||
|
/// </summary>
|
||||||
|
public int SaveBankTransactionList(string bankAccountNumber, List<BankTransactionDataset> bankTransactions)
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
var transactions = GetBankTransactionList();
|
||||||
|
foreach (var bankTrx in bankTransactions)
|
||||||
|
{
|
||||||
|
var transaction = transactions.FirstOrDefault(t => t.Id == bankTrx.Id);
|
||||||
|
if (transaction == null)
|
||||||
|
{
|
||||||
|
transaction = new BankTransaction()
|
||||||
|
{
|
||||||
|
Id = bankTrx.Id,
|
||||||
|
BookingDate = bankTrx.BookingDate,
|
||||||
|
ValueDate = bankTrx.ValueDate,
|
||||||
|
Value = (decimal)bankTrx.Value,
|
||||||
|
Text = bankTrx.Text,
|
||||||
|
BankAccountNumber = bankAccountNumber,
|
||||||
|
Debtor = bankTrx.Debtor,
|
||||||
|
Creditor = bankTrx.Creditor,
|
||||||
|
};
|
||||||
|
transactions.Add(transaction);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Save(transactions, BANK_TRANSACTIONS);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bank transaction
|
||||||
|
/// </summary>
|
||||||
|
public BankTransaction GetBankTransaction(string id)
|
||||||
|
{
|
||||||
|
var transactions = GetBankTransactionList();
|
||||||
|
return transactions.FirstOrDefault(t => t.Id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
Schaad.Accounting.Db/Repositories/BaseRepository.cs
Normal file
72
Schaad.Accounting.Db/Repositories/BaseRepository.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public abstract class BaseRepository
|
||||||
|
{
|
||||||
|
protected readonly ISettingsService settingsService;
|
||||||
|
|
||||||
|
protected BaseRepository(ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
this.settingsService = settingsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void EnsureFileExisits(string fileName)
|
||||||
|
{
|
||||||
|
string filePath = Path.Combine(settingsService.GetDbPath(), fileName);
|
||||||
|
if (File.Exists(filePath) == false)
|
||||||
|
{
|
||||||
|
var lastYearFile = Path.Combine(settingsService.GetLastYearDbPath(), fileName);
|
||||||
|
if (File.Exists(lastYearFile))
|
||||||
|
{
|
||||||
|
File.Copy(lastYearFile, filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save an object to an xml file
|
||||||
|
/// </summary>
|
||||||
|
protected void Save<T>(T obj, string fileName)
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(settingsService.GetDbPath(), fileName);
|
||||||
|
using (var sww = new MemoryStream())
|
||||||
|
{
|
||||||
|
var settings = new XmlWriterSettings
|
||||||
|
{
|
||||||
|
Encoding = Encoding.UTF8,
|
||||||
|
Indent = true
|
||||||
|
};
|
||||||
|
using (var writer = XmlWriter.Create(sww, settings))
|
||||||
|
{
|
||||||
|
var xsSubmit = new XmlSerializer(typeof(T));
|
||||||
|
xsSubmit.Serialize(writer, obj);
|
||||||
|
var xml = Encoding.UTF8.GetString(sww.ToArray());
|
||||||
|
File.WriteAllText(filePath, xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load an object from an xml file
|
||||||
|
/// </summary>
|
||||||
|
protected T Load<T>(string fileName)
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(settingsService.GetDbPath(), fileName);
|
||||||
|
if (File.Exists(filePath) == false)
|
||||||
|
{
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (XmlReader reader = XmlReader.Create(filePath))
|
||||||
|
{
|
||||||
|
var serializer = new XmlSerializer(typeof(T));
|
||||||
|
return (T)serializer.Deserialize(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Schaad.Accounting.Db/Repositories/BookingRuleRepository.cs
Normal file
64
Schaad.Accounting.Db/Repositories/BookingRuleRepository.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public class BookingRuleRepository : BaseRepository, IBookingRuleRepository
|
||||||
|
{
|
||||||
|
private readonly string BOOKING_RULES = "BookingRules.xml";
|
||||||
|
|
||||||
|
public BookingRuleRepository(ISettingsService settingsService) : base(settingsService)
|
||||||
|
{
|
||||||
|
EnsureFileExisits(BOOKING_RULES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load booking rules
|
||||||
|
/// </summary>
|
||||||
|
public List<BookingRule> GetBookingRuleList()
|
||||||
|
{
|
||||||
|
var bookingTexts = Load<List<BookingRule>>(BOOKING_RULES);
|
||||||
|
return bookingTexts ?? new List<BookingRule>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a booking rule (insert/update)
|
||||||
|
/// </summary>
|
||||||
|
public void SaveBookingRule(BookingRule bookingRule)
|
||||||
|
{
|
||||||
|
var bookingRules = GetBookingRuleList();
|
||||||
|
var existingRule = bookingRules.FirstOrDefault(a => a.Id == bookingRule.Id);
|
||||||
|
|
||||||
|
if (existingRule == null)
|
||||||
|
{
|
||||||
|
existingRule = new BookingRule();
|
||||||
|
bookingRules.Add(existingRule);
|
||||||
|
bookingRule.Id = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
bookingRule.Copy(existingRule);
|
||||||
|
Save(bookingRules, BOOKING_RULES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get booking rule
|
||||||
|
/// </summary>
|
||||||
|
public BookingRule GetBookingRule(string id)
|
||||||
|
{
|
||||||
|
var bookingRules = GetBookingRuleList();
|
||||||
|
return bookingRules.FirstOrDefault(t => t.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete booking rule
|
||||||
|
/// </summary>
|
||||||
|
public void DeleteBookingRule(string id)
|
||||||
|
{
|
||||||
|
var bookingRules = GetBookingRuleList();
|
||||||
|
bookingRules.RemoveAll(t => t.Id == id);
|
||||||
|
Save(bookingRules, BOOKING_RULES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Schaad.Accounting.Db/Repositories/BookingTextRepository.cs
Normal file
64
Schaad.Accounting.Db/Repositories/BookingTextRepository.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public class BookingTextRepository : BaseRepository, IBookingTextRepository
|
||||||
|
{
|
||||||
|
private readonly string BOOKING_TEXTS = "BookingTexts.xml";
|
||||||
|
|
||||||
|
public BookingTextRepository(ISettingsService settingsService) : base(settingsService)
|
||||||
|
{
|
||||||
|
EnsureFileExisits(BOOKING_TEXTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load bokking texts
|
||||||
|
/// </summary>
|
||||||
|
public List<BookingText> GetBookingTextList()
|
||||||
|
{
|
||||||
|
var bookingTexts = Load<List<BookingText>>(BOOKING_TEXTS);
|
||||||
|
return bookingTexts ?? new List<BookingText>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a booking text (insert/update)
|
||||||
|
/// </summary>
|
||||||
|
public void SaveBookingText(BookingText bookingText)
|
||||||
|
{
|
||||||
|
var bookingTexts = GetBookingTextList();
|
||||||
|
var existingText = bookingTexts.FirstOrDefault(a => a.Id == bookingText.Id);
|
||||||
|
|
||||||
|
if (existingText == null)
|
||||||
|
{
|
||||||
|
existingText = new BookingText();
|
||||||
|
bookingTexts.Add(existingText);
|
||||||
|
bookingText.Id = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
bookingText.Copy(existingText);
|
||||||
|
Save(bookingTexts, BOOKING_TEXTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get booking text
|
||||||
|
/// </summary>
|
||||||
|
public BookingText GetBookingText(string id)
|
||||||
|
{
|
||||||
|
var bookingTexts = GetBookingTextList();
|
||||||
|
return bookingTexts.FirstOrDefault(t => t.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete booking text
|
||||||
|
/// </summary>
|
||||||
|
public void DeleteBookingText(string id)
|
||||||
|
{
|
||||||
|
var bookingTexts = GetBookingTextList();
|
||||||
|
bookingTexts.RemoveAll(t => t.Id == id);
|
||||||
|
Save(bookingTexts, BOOKING_TEXTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public class SplitPredefinitonRepository : BaseRepository, ISplitPredefinitonRepository
|
||||||
|
{
|
||||||
|
private readonly string SPLIT_PREDEFINITION = "SplitPredefinitions.xml";
|
||||||
|
|
||||||
|
public SplitPredefinitonRepository(ISettingsService settingsService) : base(settingsService)
|
||||||
|
{
|
||||||
|
EnsureFileExisits(SPLIT_PREDEFINITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load booking rules
|
||||||
|
/// </summary>
|
||||||
|
public List<SplitPredefinition> GetSplitPredefinitionList()
|
||||||
|
{
|
||||||
|
var definitions = Load<List<SplitPredefinition>>(SPLIT_PREDEFINITION);
|
||||||
|
return definitions ?? new List<SplitPredefinition>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a booking rule (insert/update)
|
||||||
|
/// </summary>
|
||||||
|
public void SaveSplitPredefinition(SplitPredefinition splitPredefinition)
|
||||||
|
{
|
||||||
|
var definitions = GetSplitPredefinitionList();
|
||||||
|
var existingDefinition = definitions.FirstOrDefault(a => a.Id == splitPredefinition.Id);
|
||||||
|
|
||||||
|
if (existingDefinition == null)
|
||||||
|
{
|
||||||
|
existingDefinition = new SplitPredefinition();
|
||||||
|
definitions.Add(existingDefinition);
|
||||||
|
splitPredefinition.Id = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
splitPredefinition.Copy(existingDefinition);
|
||||||
|
Save(definitions, SPLIT_PREDEFINITION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
Schaad.Accounting.Db/Repositories/SubclassRepository.cs
Normal file
77
Schaad.Accounting.Db/Repositories/SubclassRepository.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public class SubclassRepository : BaseRepository, ISubclassRepository
|
||||||
|
{
|
||||||
|
private readonly Dictionary<int, string> classes = new Dictionary<int, string>();
|
||||||
|
private readonly string SUBCLASSES = "SubClasses.xml";
|
||||||
|
|
||||||
|
public SubclassRepository(ISettingsService settingsService) : base(settingsService)
|
||||||
|
{
|
||||||
|
EnsureFileExisits(SUBCLASSES);
|
||||||
|
|
||||||
|
classes.Add(ClassIds.Activa, "Aktiven");
|
||||||
|
classes.Add(ClassIds.Passiva, "Passiven");
|
||||||
|
classes.Add(ClassIds.Income, "Einnahmen");
|
||||||
|
classes.Add(ClassIds.Expenses, "Ausgaben");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load bokking texts
|
||||||
|
/// </summary>
|
||||||
|
public List<SubClass> GetSubClassList()
|
||||||
|
{
|
||||||
|
var subclasses = Load<List<SubClass>>(SUBCLASSES);
|
||||||
|
return subclasses ?? new List<SubClass>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a booking text (insert/update)
|
||||||
|
/// </summary>
|
||||||
|
public void SaveSubClass(SubClass subClass)
|
||||||
|
{
|
||||||
|
var subclasses = GetSubClassList();
|
||||||
|
var existingSubClass = subclasses.FirstOrDefault(a => a.Id == subClass.Id);
|
||||||
|
|
||||||
|
if (existingSubClass == null)
|
||||||
|
{
|
||||||
|
existingSubClass = new SubClass();
|
||||||
|
subclasses.Add(existingSubClass);
|
||||||
|
subClass.Id = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
subClass.Copy(existingSubClass);
|
||||||
|
Save(subclasses, SUBCLASSES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get subclass
|
||||||
|
/// </summary>
|
||||||
|
public SubClass GetSubClass(string id)
|
||||||
|
{
|
||||||
|
var subclasses = GetSubClassList();
|
||||||
|
return subclasses.FirstOrDefault(t => t.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteSubClass(string id)
|
||||||
|
{
|
||||||
|
var subclasses = GetSubClassList();
|
||||||
|
subclasses.RemoveAll(t => t.Id == id);
|
||||||
|
Save(subclasses, SUBCLASSES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<int, string> GetClasses()
|
||||||
|
{
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetClass(int classId)
|
||||||
|
{
|
||||||
|
return classes[classId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
Schaad.Accounting.Db/Repositories/TransactionRepository.cs
Normal file
80
Schaad.Accounting.Db/Repositories/TransactionRepository.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Repositories
|
||||||
|
{
|
||||||
|
public class TransactionRepository : BaseRepository, ITransactionRepository
|
||||||
|
{
|
||||||
|
private readonly IAccountRepository accountRepository;
|
||||||
|
private readonly string TRANSACTIONS = "Transactions.xml";
|
||||||
|
|
||||||
|
public TransactionRepository(ISettingsService settingsService, IAccountRepository accountRepository) : base(settingsService)
|
||||||
|
{
|
||||||
|
this.accountRepository = accountRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get transaction list
|
||||||
|
/// </summary>
|
||||||
|
public List<Transaction> GetTransactionList()
|
||||||
|
{
|
||||||
|
var transactionList = Load<List<Transaction>>(TRANSACTIONS);
|
||||||
|
return transactionList ?? new List<Transaction>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a transaction (insert/update)
|
||||||
|
/// </summary>
|
||||||
|
public void SaveTransaction(Transaction transaction)
|
||||||
|
{
|
||||||
|
// prevent saving fxrate for chf account
|
||||||
|
var isFxAccount = accountRepository.GetAccount(transaction.OriginAccountId).IsFxAccount;
|
||||||
|
|
||||||
|
// if source account is fx account, we pass a foreign value -> calc to chf
|
||||||
|
if (isFxAccount && transaction.FxRate != 0)
|
||||||
|
{
|
||||||
|
transaction.Value = transaction.Value * transaction.FxRate.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFxAccount |= accountRepository.GetAccount(transaction.TargetAccountId).IsFxAccount;
|
||||||
|
if (isFxAccount == false)
|
||||||
|
{
|
||||||
|
transaction.FxRate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var transactionList = GetTransactionList();
|
||||||
|
var existingTransaction = transactionList.FirstOrDefault(a => a.Id == transaction.Id);
|
||||||
|
|
||||||
|
if (existingTransaction == null)
|
||||||
|
{
|
||||||
|
existingTransaction = new Transaction();
|
||||||
|
transactionList.Add(existingTransaction);
|
||||||
|
transaction.Id = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
transaction.Copy(existingTransaction);
|
||||||
|
Save(transactionList, TRANSACTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get transaction
|
||||||
|
/// </summary>
|
||||||
|
public Transaction GetTransaction(string id)
|
||||||
|
{
|
||||||
|
var transactions = GetTransactionList();
|
||||||
|
var transaction = transactions.FirstOrDefault(t => t.Id == id);
|
||||||
|
|
||||||
|
// if source account is fx account, we save in chf currency -> calc to foreign currency for display
|
||||||
|
var isFxAccount = accountRepository.GetAccount(transaction.OriginAccountId).IsFxAccount;
|
||||||
|
if (isFxAccount && transaction.FxRate != 0)
|
||||||
|
{
|
||||||
|
transaction.Value = transaction.Value / transaction.FxRate.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Schaad.Accounting.Db/Schaad.Accounting.Db.csproj
Normal file
13
Schaad.Accounting.Db/Schaad.Accounting.Db.csproj
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Schaad.Accounting.Common\Schaad.Accounting.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
130
Schaad.Accounting.Services/ChartService.cs
Normal file
130
Schaad.Accounting.Services/ChartService.cs
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Datasets.Charts;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Services
|
||||||
|
{
|
||||||
|
public class ChartService : IChartService
|
||||||
|
{
|
||||||
|
private readonly IAccountRepository accountRepository;
|
||||||
|
private readonly ISettingsService settingsService;
|
||||||
|
private readonly ISubclassRepository subclassRepository;
|
||||||
|
private readonly IViewService viewService;
|
||||||
|
|
||||||
|
public ChartService(
|
||||||
|
ISettingsService settingsService,
|
||||||
|
IViewService viewService,
|
||||||
|
IAccountRepository accountRepository,
|
||||||
|
ISubclassRepository subclassRepository)
|
||||||
|
{
|
||||||
|
this.settingsService = settingsService;
|
||||||
|
this.viewService = viewService;
|
||||||
|
this.accountRepository = accountRepository;
|
||||||
|
this.subclassRepository = subclassRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DataSerie> GetExpensesPerMonth()
|
||||||
|
{
|
||||||
|
var transactions = viewService.GetTransactionViewList().Where(a => a.TargetAccount.Class == ClassIds.Expenses);
|
||||||
|
|
||||||
|
// More than one subclass xx
|
||||||
|
if (transactions.Select(t => t.TargetAccount.SubClass).Distinct().Count() > 1)
|
||||||
|
{
|
||||||
|
return GetSubClassExpensesPerMonth();
|
||||||
|
}
|
||||||
|
|
||||||
|
// only one subclass xx (z.B. Mandant Mannenbach)
|
||||||
|
return GetAccountExpensesPerMonth();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DataSerie> GetSubClassExpensesPerMonth()
|
||||||
|
{
|
||||||
|
//var subclasses = subclassRepository.GetSubClassList();
|
||||||
|
var transactions = viewService.GetTransactionViewList().Where(a => a.TargetAccount.Class == ClassIds.Expenses).ToList();
|
||||||
|
var list = new List<DataSerie>();
|
||||||
|
|
||||||
|
var newestTransaction = transactions.OrderByDescending(t => t.ValueDate).FirstOrDefault();
|
||||||
|
var maxMonth = newestTransaction?.ValueDate.Month ?? 12;
|
||||||
|
var year = settingsService.GetYear();
|
||||||
|
|
||||||
|
// Group by subclass
|
||||||
|
foreach (var grp in transactions.GroupBy(a => a.TargetAccount.SubClass).Select(a => new {Key = a.Key, List = a.ToList()}))
|
||||||
|
{
|
||||||
|
// Sum subclass transactions per month
|
||||||
|
var groupedByMonth = grp.List.GroupBy(g => g.ValueDate.Month).ToDictionary(g => g.Key, g => g.ToList().Sum(s => s.Value));
|
||||||
|
EnsureEntryForEveryMonth(groupedByMonth, maxMonth);
|
||||||
|
|
||||||
|
//var subClass = subclasses.FirstOrDefault(s => s.Number == grp.List.First().TargetAccount.SubClass);
|
||||||
|
list.Add(
|
||||||
|
new DataSerie(
|
||||||
|
Id: grp.List.First().TargetAccount.SubClass.ToString(),
|
||||||
|
Name: grp.List.First().TargetAccount.Name,
|
||||||
|
X: groupedByMonth.OrderBy(g => g.Key).Select(g => new DateOnly(year, g.Key, 1)).ToList(),
|
||||||
|
Y: groupedByMonth.OrderBy(g => g.Key).Select(g => g.Value).ToList()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DataSerie> GetAccountExpensesPerMonth()
|
||||||
|
{
|
||||||
|
var list = new List<DataSerie>();
|
||||||
|
var expensesAccounts = viewService.GetAccountViewList().Where(a => a.Class == ClassIds.Expenses);
|
||||||
|
foreach (var account in expensesAccounts)
|
||||||
|
{
|
||||||
|
var serie = GetAccountExpensesPerMonth(account.Id, DateTime.Now.Year);
|
||||||
|
if (serie != null)
|
||||||
|
{
|
||||||
|
list.Add(serie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataSerie GetAccountExpensesPerMonth(string accountId, int year)
|
||||||
|
{
|
||||||
|
if (settingsService.TrySetYear(year))
|
||||||
|
{
|
||||||
|
var accountList = accountRepository.GetAccountList();
|
||||||
|
var account = accountList.SingleOrDefault(a => a.Id == accountId);
|
||||||
|
// perhaps we dont have the account for last year
|
||||||
|
if (account == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var transactions = viewService.GetTransactionViewList().Where(t => t.TargetAccountId == accountId).ToList();
|
||||||
|
|
||||||
|
var newestTransaction = transactions.OrderByDescending(t => t.ValueDate).FirstOrDefault();
|
||||||
|
var maxMonth = newestTransaction?.ValueDate.Month ?? 12;
|
||||||
|
|
||||||
|
var groupedByMonth = transactions.GroupBy(g => g.ValueDate.Month).ToDictionary(g => g.Key, g => g.ToList().Sum(s => s.Value));
|
||||||
|
EnsureEntryForEveryMonth(groupedByMonth, maxMonth);
|
||||||
|
|
||||||
|
settingsService.SetYear(DateTime.Now.Year);
|
||||||
|
|
||||||
|
return new DataSerie(
|
||||||
|
Id: accountId,
|
||||||
|
Name: account.Name,
|
||||||
|
X: groupedByMonth.OrderBy(g => g.Key).Select(g => new DateOnly(year, g.Key, 1)).ToList(),
|
||||||
|
Y: groupedByMonth.OrderBy(g => g.Key).Select(g => g.Value).ToList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureEntryForEveryMonth(Dictionary<int, decimal> values, int maxMonth = 12)
|
||||||
|
{
|
||||||
|
for (int i = 1; i <= maxMonth; i++)
|
||||||
|
{
|
||||||
|
if (values.ContainsKey(i) == false)
|
||||||
|
{
|
||||||
|
values.Add(i, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Schaad.Accounting.Services/DummyFxService.cs
Normal file
26
Schaad.Accounting.Services/DummyFxService.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
using Schaad.Finance.Api;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Services
|
||||||
|
{
|
||||||
|
public class DummyFxService : IFxService
|
||||||
|
{
|
||||||
|
private List<string> currencies = ["CHF", "EUR", "USD"];
|
||||||
|
|
||||||
|
public decimal ConvertCurrency(decimal amount, string fromCurrency, string toCurrency, string fixerIoApiKey)
|
||||||
|
{
|
||||||
|
if (currencies.Contains(fromCurrency) && currencies.Contains(fromCurrency))
|
||||||
|
{
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException($"Cannot convert from {fromCurrency} to {toCurrency}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public decimal GetFxRate(string fromCurrency, string toCurrency, DateTime date, string fixerIoApiKey)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
150
Schaad.Accounting.Services/FileService.cs
Normal file
150
Schaad.Accounting.Services/FileService.cs
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Schaad.Accounting.Common.Extensions;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Finance.Api;
|
||||||
|
using Schaad.Finance.Api.Datasets;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Services
|
||||||
|
{
|
||||||
|
public class FileService : IFileService
|
||||||
|
{
|
||||||
|
private readonly IAccountRepository accountRepository;
|
||||||
|
private readonly ITransactionRepository transactionsRepository;
|
||||||
|
private readonly IBankTransactionRepository bankTransactionRepository;
|
||||||
|
private readonly ISettingsService settingsService;
|
||||||
|
private readonly IAccountStatementService accountStatementService;
|
||||||
|
private readonly ICreditCardStatementService creditCardStatementService;
|
||||||
|
|
||||||
|
public FileService(
|
||||||
|
ISettingsService settingsService,
|
||||||
|
IAccountRepository accountRepository,
|
||||||
|
ITransactionRepository transactionsRepository,
|
||||||
|
IBankTransactionRepository bankTransactionRepository,
|
||||||
|
IAccountStatementService accountStatementService,
|
||||||
|
ICreditCardStatementService creditCardStatementService)
|
||||||
|
{
|
||||||
|
this.settingsService = settingsService;
|
||||||
|
this.accountRepository = accountRepository;
|
||||||
|
this.transactionsRepository = transactionsRepository;
|
||||||
|
this.bankTransactionRepository = bankTransactionRepository;
|
||||||
|
this.accountStatementService = accountStatementService;
|
||||||
|
this.creditCardStatementService = creditCardStatementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Backup()
|
||||||
|
{
|
||||||
|
var outpath = settingsService.GetBackupFilePath();
|
||||||
|
var sourcepath = settingsService.GetYearMandatorPath();
|
||||||
|
ZipFile.CreateFromDirectory(sourcepath, outpath);
|
||||||
|
return $"Backup '{outpath}' erfolgreich erstellt";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload account statement file (mt940, camt053)
|
||||||
|
// http://www.mikesdotnetting.com/article/288/asp-net-5-uploading-files-with-asp-net-mvc-6
|
||||||
|
public IReadOnlyList<MessageDataset> ImportAccountStatementFile(string filePath)
|
||||||
|
{
|
||||||
|
var messages = new List<MessageDataset>();
|
||||||
|
var accountStatementResults = accountStatementService.ReadFile(filePath, Encoding.UTF8);
|
||||||
|
|
||||||
|
var accountList = accountRepository.GetAccountList().Select(a => a.BankAccountNumber).ToList();
|
||||||
|
foreach (var accountStatementResult in accountStatementResults)
|
||||||
|
{
|
||||||
|
var account = accountStatementResult.AccountStatement;
|
||||||
|
|
||||||
|
if (accountStatementResult.IsSuccess == false)
|
||||||
|
{
|
||||||
|
messages.Add(new MessageDataset($"Account {account.AccountNumber} NICHT importiert: {accountStatementResult.Error}", MessageStatus.Error));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountStatementResult.AccountStatement.Transactions.Any() == false)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = new MessageDataset($"Konto {account.AccountNumber}");
|
||||||
|
messages.Add(message);
|
||||||
|
|
||||||
|
// only accounts that are in select mandator
|
||||||
|
if (accountList.Contains(account.AccountNumber))
|
||||||
|
{
|
||||||
|
var transactionsThisYear = account.Transactions
|
||||||
|
.Where(t => t.BookingDate.Year == settingsService.GetYear())
|
||||||
|
.Select(
|
||||||
|
t => new BankTransactionDataset
|
||||||
|
{
|
||||||
|
Id = t.Id,
|
||||||
|
Value = (decimal)t.Value,
|
||||||
|
ValueDate = t.ValueDate,
|
||||||
|
BookingDate = t.BookingDate,
|
||||||
|
Text = t.Text,
|
||||||
|
Debtor = t.Debtor,
|
||||||
|
Creditor = t.Creditor
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
var count = bankTransactionRepository.SaveBankTransactionList(account.AccountNumber, transactionsThisYear);
|
||||||
|
accountRepository.SaveBankAccountBalance(account.AccountNumber, (decimal)account.EndBalance.Value);
|
||||||
|
|
||||||
|
var status = count == account.Transactions.Count ? MessageStatus.Success : MessageStatus.Info;
|
||||||
|
message.Add($"{count} von {account.Transactions.Count} Transaktion(en) importiert.", status);
|
||||||
|
|
||||||
|
// not imported transactions
|
||||||
|
var notImportedTransactions = account.Transactions.Where(t => transactionsThisYear.Select(tt => tt.Id).Contains(t.Id) == false);
|
||||||
|
foreach (var trx in notImportedTransactions)
|
||||||
|
{
|
||||||
|
message.Add($"Transaktion wegen BookingDatum {trx.BookingDate} nicht importiert: {trx.Text}", MessageStatus.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message.Add($"Falscher Mandant: {account.Transactions.Count} Transaktion(en) nicht importiert.", MessageStatus.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<CreditCardTransaction> ImportCreditCardStatementFile(CreditCardProvider creditCardProvider, string filePath)
|
||||||
|
{
|
||||||
|
var creditCardTransactions = creditCardStatementService.ReadFile(creditCardProvider, filePath, Encoding.UTF8);
|
||||||
|
return creditCardTransactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetTransactionListCsv(string accountId)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var account = accountRepository.GetAccount(accountId);
|
||||||
|
var transactions = transactionsRepository.GetTransactionList()
|
||||||
|
.Where(t => t.OriginAccountId == accountId || t.TargetAccountId == accountId)
|
||||||
|
.OrderBy(t => t.BookingDate)
|
||||||
|
.ThenBy(t => t.ValueDate)
|
||||||
|
.ThenBy(t => t.Value)
|
||||||
|
.ToList();
|
||||||
|
var balance = account.StartBalance;
|
||||||
|
sb.AppendLine($"Buchungsdatum;Valuta;Buchungstext;Belastung;Gutschrift;Saldo {account.Currency}");
|
||||||
|
sb.AppendLine($";;Startsaldo;;;{balance}");
|
||||||
|
foreach (var trx in transactions)
|
||||||
|
{
|
||||||
|
var credit = "";
|
||||||
|
var debit = "";
|
||||||
|
|
||||||
|
if (trx.OriginAccountId == accountId)
|
||||||
|
{
|
||||||
|
debit = trx.Value.ToFormattedString();
|
||||||
|
trx.Value *= -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
credit = trx.Value.ToFormattedString();
|
||||||
|
}
|
||||||
|
balance += trx.Value;
|
||||||
|
sb.AppendLine($"{trx.BookingDate:dd.MM.yyyy};{trx.ValueDate:dd.MM.yyyy};{trx.Text};{debit};{credit};{balance.ToFormattedString()}");
|
||||||
|
}
|
||||||
|
var fileBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(sb.ToString());
|
||||||
|
return fileBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Schaad.Accounting.Services/Schaad.Accounting.Services.csproj
Normal file
22
Schaad.Accounting.Services/Schaad.Accounting.Services.csproj
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FreeSpire.PDF" Version="10.2.0" />
|
||||||
|
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Schaad.Accounting.Common\Schaad.Accounting.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Schaad.Finance">
|
||||||
|
<HintPath>..\dlls\Schaad.Finance.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Schaad.Finance.Api">
|
||||||
|
<HintPath>..\dlls\Schaad.Finance.Api.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
124
Schaad.Accounting.Services/SettingsService.cs
Normal file
124
Schaad.Accounting.Services/SettingsService.cs
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Services
|
||||||
|
{
|
||||||
|
public class SettingsService : ISettingsService
|
||||||
|
{
|
||||||
|
private readonly SettingsDataset settingsDataset;
|
||||||
|
private string mandator = "Claudio Schaad";
|
||||||
|
private int year = DateTime.Now.Year;
|
||||||
|
|
||||||
|
public SettingsService(SettingsDataset settingsDataset)
|
||||||
|
{
|
||||||
|
this.settingsDataset = settingsDataset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetYear()
|
||||||
|
{
|
||||||
|
return year;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetYear(int year)
|
||||||
|
{
|
||||||
|
this.year = year;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TrySetYear(int year)
|
||||||
|
{
|
||||||
|
var oldYear = year;
|
||||||
|
SetYear(year);
|
||||||
|
if (Directory.GetFiles(GetDbPath()).Any() == false)
|
||||||
|
{
|
||||||
|
SetYear(oldYear);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetMandator()
|
||||||
|
{
|
||||||
|
return mandator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMandator(string mandator)
|
||||||
|
{
|
||||||
|
this.mandator = mandator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetYearMandatorPath()
|
||||||
|
{
|
||||||
|
var path = Path.Combine(GetSettings().DataPath, year.ToString(), mandator);
|
||||||
|
if (Directory.Exists(path) == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLastYearMandatorPath()
|
||||||
|
{
|
||||||
|
var path = Path.Combine(GetSettings().DataPath, (year - 1).ToString(), mandator);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetBackupFilePath()
|
||||||
|
{
|
||||||
|
var backupPath = Path.Combine(GetSettings().DataPath, year.ToString(), "backup", mandator);
|
||||||
|
if (Directory.Exists(backupPath) == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(backupPath);
|
||||||
|
}
|
||||||
|
var backupFilePath = Path.Combine(backupPath, DateTime.Now.ToString("yyyyMMddHHmmss") + ".zip");
|
||||||
|
return backupFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetUploadPath()
|
||||||
|
{
|
||||||
|
var uploadPath = Path.Combine(GetYearMandatorPath(), "uploads");
|
||||||
|
if (Directory.Exists(uploadPath) == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(uploadPath);
|
||||||
|
}
|
||||||
|
return uploadPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCreditCardStatementPath()
|
||||||
|
{
|
||||||
|
var path = Path.Combine(GetUploadPath(), "creditCard");
|
||||||
|
if (Directory.Exists(path) == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDbPath()
|
||||||
|
{
|
||||||
|
var path = Path.Combine(GetYearMandatorPath(), "Data");
|
||||||
|
if (Directory.Exists(path) == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLastYearDbPath()
|
||||||
|
{
|
||||||
|
var path = Path.Combine(GetLastYearMandatorPath(), "Data");
|
||||||
|
if (Directory.Exists(path) == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SettingsDataset GetSettings()
|
||||||
|
{
|
||||||
|
return settingsDataset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
355
Schaad.Accounting.Services/ViewService.cs
Normal file
355
Schaad.Accounting.Services/ViewService.cs
Normal file
|
|
@ -0,0 +1,355 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Datasets.Reports;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
using Schaad.Finance.Api;
|
||||||
|
using Schaad.Finance.Api.Datasets;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.Services
|
||||||
|
{
|
||||||
|
public class ViewService : IViewService
|
||||||
|
{
|
||||||
|
private readonly IAccountRepository accountRepository;
|
||||||
|
private readonly IBankTransactionRepository bankTransactionRepository;
|
||||||
|
private readonly IBookingRuleRepository bookingRuleRepository;
|
||||||
|
private readonly ISubclassRepository subclassRepository;
|
||||||
|
private readonly ITransactionRepository transactionRepository;
|
||||||
|
private readonly IFxService fxService;
|
||||||
|
private readonly ISettingsService settingsService;
|
||||||
|
|
||||||
|
public ViewService(
|
||||||
|
IAccountRepository accountRepository,
|
||||||
|
IBankTransactionRepository bankTransactionRepository,
|
||||||
|
ITransactionRepository transactionRepository,
|
||||||
|
ISubclassRepository subclassRepository,
|
||||||
|
IBookingRuleRepository bookingRuleRepository,
|
||||||
|
IFxService fxService,
|
||||||
|
ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
this.accountRepository = accountRepository;
|
||||||
|
this.bankTransactionRepository = bankTransactionRepository;
|
||||||
|
this.transactionRepository = transactionRepository;
|
||||||
|
this.subclassRepository = subclassRepository;
|
||||||
|
this.bookingRuleRepository = bookingRuleRepository;
|
||||||
|
this.fxService = fxService;
|
||||||
|
this.settingsService = settingsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load accounts
|
||||||
|
/// </summary>
|
||||||
|
public AccountDataset GetAccountView(string id)
|
||||||
|
{
|
||||||
|
var accounts = GetAccountViewList();
|
||||||
|
return accounts.Single(t => t.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load accounts and calculates their balances
|
||||||
|
/// </summary>
|
||||||
|
public List<AccountDataset> GetAccountViewList()
|
||||||
|
{
|
||||||
|
var accounts = accountRepository.GetAccountList();
|
||||||
|
var transactionList = GetTransactionViewList();
|
||||||
|
var subClassNameByNumber = subclassRepository.GetSubClassList().ToDictionary(s => s.Number, s => s.Name);
|
||||||
|
var settings = settingsService.GetSettings();
|
||||||
|
|
||||||
|
var accountList = accounts.Select(
|
||||||
|
a =>
|
||||||
|
new AccountDataset(
|
||||||
|
account: a,
|
||||||
|
balance: GetBalanceInAccountCurrency(a, transactionList),
|
||||||
|
balanceCHF: GetCurrentBalanceInCHF(a, transactionList),
|
||||||
|
startBalanceCHF: fxService.ConvertCurrency(a.StartBalance, a.Currency, "CHF", settings.FixerIoApiKey),
|
||||||
|
className: subclassRepository.GetClass(a.Class),
|
||||||
|
subClassName: subClassNameByNumber[a.SubClass]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return accountList.OrderBy(a => a.Number).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BalanceDataset GetBalanceView()
|
||||||
|
{
|
||||||
|
var accountList = GetAccountViewList();
|
||||||
|
var balanceView = new BalanceDataset(
|
||||||
|
activaAccountList: accountList.Where(m => m.Class == ClassIds.Activa).ToList(),
|
||||||
|
passivaAccountList: accountList.Where(m => m.Class == ClassIds.Passiva).ToList(),
|
||||||
|
totalActivaCHF: Math.Abs(accountList.Where(m => m.Class == ClassIds.Activa).Sum(m => m.BalanceCHF)),
|
||||||
|
totalPassivaCHF: Math.Abs(accountList.Where(m => m.Class == ClassIds.Passiva).Sum(m => m.BalanceCHF))
|
||||||
|
);
|
||||||
|
|
||||||
|
return balanceView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BalanceSheetDataset GetBalanceSheetView(int year)
|
||||||
|
{
|
||||||
|
var accountList = GetAccountViewList();
|
||||||
|
|
||||||
|
var settings = settingsService.GetSettings();
|
||||||
|
var profit = Math.Abs(accountList.Where(m => m.Class == ClassIds.Income).Sum(m =>m.BalanceCHF));
|
||||||
|
profit += Math.Abs(accountList.Where(m => m.Class == ClassIds.Activa).Sum(m => fxService.ConvertCurrency(m.StartBalance, m.Currency, "CHF", settings.FixerIoApiKey)));
|
||||||
|
var loss = Math.Abs(accountList.Where(m => m.Class == ClassIds.Expenses).Sum(m => m.BalanceCHF));
|
||||||
|
|
||||||
|
var balanceView = new BalanceSheetDataset(
|
||||||
|
activaAccountList: accountList.Where(m => m.Class == ClassIds.Activa).ToList(),
|
||||||
|
incomeAccountList: accountList.Where(m => m.Class == ClassIds.Income).ToList(),
|
||||||
|
expensesAccountList: accountList.Where(m => m.Class == ClassIds.Expenses).ToList(),
|
||||||
|
profitCHF: profit,
|
||||||
|
lossCHF: loss,
|
||||||
|
year: year
|
||||||
|
);
|
||||||
|
|
||||||
|
return balanceView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private decimal GetBalanceInAccountCurrency(Account a, List<TransactionDataset> transactionList)
|
||||||
|
{
|
||||||
|
var balance = a.StartBalance
|
||||||
|
+ transactionList.Where(t => t.TargetAccountId == a.Id).Sum(t => t.GetValue(a.IsFxAccount))
|
||||||
|
- transactionList.Where(t => t.OriginAccountId == a.Id).Sum(t => t.GetValue(a.IsFxAccount));
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private decimal GetCurrentBalanceInCHF(Account a, List<TransactionDataset> transactionList)
|
||||||
|
{
|
||||||
|
var settings = settingsService.GetSettings();
|
||||||
|
var balanceInAccountCurrency = GetBalanceInAccountCurrency(a, transactionList);
|
||||||
|
var balanceInChf = fxService.ConvertCurrency(balanceInAccountCurrency, a.Currency, "CHF", settings.FixerIoApiKey);
|
||||||
|
return balanceInChf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get transaction list with the origin and target account for each transaction
|
||||||
|
/// </summary>
|
||||||
|
public List<TransactionDataset> GetTransactionViewList()
|
||||||
|
{
|
||||||
|
var accountList = accountRepository.GetAccountList();
|
||||||
|
var transactionList = transactionRepository.GetTransactionList();
|
||||||
|
|
||||||
|
var transactionViewList = transactionList.Select(
|
||||||
|
t =>
|
||||||
|
new TransactionDataset(
|
||||||
|
t,
|
||||||
|
accountList.Single(a => a.Id == t.OriginAccountId),
|
||||||
|
accountList.Single(a => a.Id == t.TargetAccountId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return transactionViewList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get transaction list with the origin and target account for each transaction
|
||||||
|
/// </summary>
|
||||||
|
public List<TransactionDataset> GetTransactionViewList(string accountId)
|
||||||
|
{
|
||||||
|
var accountList = accountRepository.GetAccountList();
|
||||||
|
var transactionList = transactionRepository.GetTransactionList().Where(t => t.OriginAccountId == accountId || t.TargetAccountId == accountId);
|
||||||
|
|
||||||
|
var transactionViewList = transactionList.Select(
|
||||||
|
t =>
|
||||||
|
new TransactionDataset(
|
||||||
|
t,
|
||||||
|
accountList.Single(a => a.Id == t.OriginAccountId),
|
||||||
|
accountList.Single(a => a.Id == t.TargetAccountId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return transactionViewList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get booking rules with their account
|
||||||
|
/// </summary>
|
||||||
|
public List<BookingRuleDataset> GetBookingRuleViewList()
|
||||||
|
{
|
||||||
|
var accountList = accountRepository.GetAccountList();
|
||||||
|
var bookinRuleList = bookingRuleRepository.GetBookingRuleList();
|
||||||
|
|
||||||
|
return bookinRuleList.Select(
|
||||||
|
t =>
|
||||||
|
new BookingRuleDataset(
|
||||||
|
t,
|
||||||
|
accountList.Single(a => a.Id == t.AccountId).Name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load open bank transactions
|
||||||
|
/// </summary>
|
||||||
|
public List<BankTransaction> GetOpenBankTransactionList()
|
||||||
|
{
|
||||||
|
var transactions = transactionRepository
|
||||||
|
.GetTransactionList()
|
||||||
|
.Where(t => string.IsNullOrEmpty(t.BankTransactionId) == false)
|
||||||
|
.Select(t => t.BankTransactionId);
|
||||||
|
|
||||||
|
var bankTransactions = bankTransactionRepository
|
||||||
|
.GetBankTransactionList()
|
||||||
|
.Where(b => b.Ignore == false && transactions.Contains(b.Id) == false)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return bankTransactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Transaction> MatchOpenBankTransactions()
|
||||||
|
{
|
||||||
|
var newTransactionList = new List<Transaction>();
|
||||||
|
var transactions = transactionRepository.GetTransactionList();
|
||||||
|
var accounts = accountRepository.GetAccountList();
|
||||||
|
var bookingRules = bookingRuleRepository.GetBookingRuleList();
|
||||||
|
var bankTransactions = GetOpenBankTransactionList().OrderBy(t => t.ValueDate);
|
||||||
|
foreach (var bankTransaction in bankTransactions)
|
||||||
|
{
|
||||||
|
var trx = new Transaction(bankTransaction, accounts);
|
||||||
|
MatchBankTransactionByBookingRule(bankTransaction.Text, trx, bookingRules);
|
||||||
|
MatchBankTransactionBySameAccountsLastMonth(bankTransaction, trx, transactions);
|
||||||
|
MatchBankTransactionBySameValueLastMonth(bankTransaction, trx, transactions);
|
||||||
|
newTransactionList.Add(trx);
|
||||||
|
}
|
||||||
|
return newTransactionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Transaction> MatchCreditCardTransactions(string bankTransactionId, IReadOnlyList<CreditCardTransaction> creditCardTransactions)
|
||||||
|
{
|
||||||
|
var accounts = accountRepository.GetAccountList();
|
||||||
|
var bankTrx = bankTransactionRepository.GetBankTransaction(bankTransactionId);
|
||||||
|
var bookingRules = bookingRuleRepository.GetBookingRuleList();
|
||||||
|
|
||||||
|
var trxList = new List<Transaction>();
|
||||||
|
foreach (var creditCardTransaction in creditCardTransactions)
|
||||||
|
{
|
||||||
|
var trx = new Transaction(bankTrx, accounts);
|
||||||
|
trx.Value = -1 * creditCardTransaction.Amount;
|
||||||
|
trx.BookingDate = bankTrx.BookingDate;
|
||||||
|
trx.ValueDate = creditCardTransaction.TransactionDate;
|
||||||
|
|
||||||
|
MatchBankTransactionByBookingRule(creditCardTransaction.Transaction, trx, bookingRules);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(trx.Text))
|
||||||
|
{
|
||||||
|
trx.Text = creditCardTransaction.Transaction;
|
||||||
|
}
|
||||||
|
trxList.Add(trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trxList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MatchBankTransactionByBookingRule(string transactionText, Transaction trx, List<BookingRule> bookingRules)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(trx.Text) == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// all booking rules that match the text
|
||||||
|
var matchedRules = bookingRules.Where(r => transactionText.ToLowerInvariant().Contains(r.LookupText.ToLowerInvariant())).ToList();
|
||||||
|
|
||||||
|
// then match by value
|
||||||
|
var matchedRule = matchedRules.FirstOrDefault(r => r.LookupValue == Math.Abs(trx.Value));
|
||||||
|
|
||||||
|
// if no found, take match without a value
|
||||||
|
if (matchedRule == null)
|
||||||
|
{
|
||||||
|
matchedRule = matchedRules.FirstOrDefault(r => r.LookupValue == 0);
|
||||||
|
}
|
||||||
|
if (matchedRule != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(trx.OriginAccountId))
|
||||||
|
{
|
||||||
|
trx.OriginAccountId = matchedRule.AccountId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trx.TargetAccountId = matchedRule.AccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.Text = matchedRule.BookingText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MatchBankTransactionBySameAccountsLastMonth(BankTransaction bankTransaction, Transaction trx, List<Transaction> transactions)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(trx.Text) == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lastMonth = trx.ValueDate.AddMonths(-1);
|
||||||
|
var lastMonthStart = new DateTime(lastMonth.Year, lastMonth.Month, 1);
|
||||||
|
var lastMonthEnd = lastMonthStart.AddMonths(1).AddMinutes(-1);
|
||||||
|
|
||||||
|
// Find all bookings for the same accounts of the last month
|
||||||
|
var lastMonthTransactions = transactions.Where(
|
||||||
|
t =>
|
||||||
|
t.OriginAccountId == trx.OriginAccountId &&
|
||||||
|
t.TargetAccountId == trx.TargetAccountId &&
|
||||||
|
Math.Abs(t.Value) == Math.Abs(trx.Value) &&
|
||||||
|
t.ValueDate >= lastMonthStart &&
|
||||||
|
t.ValueDate <= lastMonthEnd)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
MatchSameOrOtherDay(trx, lastMonthTransactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MatchBankTransactionBySameValueLastMonth(BankTransaction bankTransaction, Transaction trx, List<Transaction> transactions)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(trx.Text) == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lastMonth = trx.ValueDate.AddMonths(-1);
|
||||||
|
var lastMonthStart = new DateTime(lastMonth.Year, lastMonth.Month, 1);
|
||||||
|
var lastMonthEnd = lastMonthStart.AddMonths(1).AddMinutes(-1);
|
||||||
|
|
||||||
|
// Find all bookings for the same amount of the last month
|
||||||
|
var lastMonthTransactions = transactions.Where(
|
||||||
|
t =>
|
||||||
|
Math.Abs(t.Value) == Math.Abs(trx.Value) &&
|
||||||
|
t.ValueDate >= lastMonthStart &&
|
||||||
|
t.ValueDate <= lastMonthEnd)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
MatchSameOrOtherDay(trx, lastMonthTransactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MatchSameOrOtherDay(Transaction trx, List<Transaction> lastMonthTransactions)
|
||||||
|
{
|
||||||
|
if (lastMonthTransactions.Any())
|
||||||
|
{
|
||||||
|
var lastMonthTransaction = lastMonthTransactions.Where(
|
||||||
|
t =>
|
||||||
|
t.ValueDate.Day == trx.ValueDate.Day)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (lastMonthTransaction == null)
|
||||||
|
{
|
||||||
|
lastMonthTransaction = lastMonthTransactions.Where(
|
||||||
|
t =>
|
||||||
|
t.ValueDate.Day >= trx.ValueDate.AddDays(-1).Day &&
|
||||||
|
t.ValueDate.Day <= trx.ValueDate.AddDays(1).Day)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastMonthTransaction != null)
|
||||||
|
{
|
||||||
|
trx.Text = lastMonthTransaction.Text;
|
||||||
|
if (string.IsNullOrEmpty(trx.OriginAccountId))
|
||||||
|
{
|
||||||
|
trx.OriginAccountId = lastMonthTransaction.OriginAccountId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trx.TargetAccountId = lastMonthTransaction.TargetAccountId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Schaad.Accounting.UI/Components/App.razor
Normal file
21
Schaad.Accounting.UI/Components/App.razor
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<base href="/" />
|
||||||
|
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
|
||||||
|
<link rel="stylesheet" href="@Assets["app.css"]" />
|
||||||
|
<link rel="stylesheet" href="@Assets["Schaad.Accounting.UI.styles.css"]" />
|
||||||
|
<ImportMap />
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
|
<HeadOutlet />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<Routes />
|
||||||
|
<script src="_framework/blazor.web.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
29
Schaad.Accounting.UI/Components/Layout/MainLayout.razor
Normal file
29
Schaad.Accounting.UI/Components/Layout/MainLayout.razor
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
|
<FluentLayout>
|
||||||
|
<FluentHeader Class="hidePrint">
|
||||||
|
AccountingNext
|
||||||
|
</FluentHeader>
|
||||||
|
<FluentStack Class="main" Orientation="Orientation.Horizontal" Width="100%">
|
||||||
|
<NavMenu />
|
||||||
|
<FluentBodyContent Class="body-content">
|
||||||
|
<div class="content">
|
||||||
|
@Body
|
||||||
|
</div>
|
||||||
|
</FluentBodyContent>
|
||||||
|
</FluentStack>
|
||||||
|
|
||||||
|
<FluentFooter Class="hidePrint">
|
||||||
|
AccountingNext App
|
||||||
|
<FluentSpacer/>
|
||||||
|
Copyright 2025 by Claudio Schaad
|
||||||
|
</FluentFooter>
|
||||||
|
</FluentLayout>
|
||||||
|
|
||||||
|
<FluentDialogProvider @rendermode="InteractiveServer"/>
|
||||||
|
|
||||||
|
<div id="blazor-error-ui" data-nosnippet>
|
||||||
|
An unhandled error has occurred.
|
||||||
|
<a href="." class="reload">Reload</a>
|
||||||
|
<span class="dismiss">🗙</span>
|
||||||
|
</div>
|
||||||
98
Schaad.Accounting.UI/Components/Layout/MainLayout.razor.css
Normal file
98
Schaad.Accounting.UI/Components/Layout/MainLayout.razor.css
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
.page {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-bottom: 1px solid #d6d5d5;
|
||||||
|
justify-content: flex-end;
|
||||||
|
height: 3.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a:first-child {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640.98px) {
|
||||||
|
.top-row {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 641px) {
|
||||||
|
.page {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
height: 100vh;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row.auth ::deep a:first-child {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row, article {
|
||||||
|
padding-left: 2rem !important;
|
||||||
|
padding-right: 1.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui {
|
||||||
|
color-scheme: light only;
|
||||||
|
background: lightyellow;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: none;
|
||||||
|
left: 0;
|
||||||
|
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui .dismiss {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.75rem;
|
||||||
|
top: 0.5rem;
|
||||||
|
}
|
||||||
35
Schaad.Accounting.UI/Components/Layout/NavMenu.razor
Normal file
35
Schaad.Accounting.UI/Components/Layout/NavMenu.razor
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
@using Microsoft.FluentUI.AspNetCore.Components.Icons.Regular
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<!-- https://www.fluentui-blazor.net/Icon#explorer -->
|
||||||
|
|
||||||
|
<div class="navmenu hidePrint">
|
||||||
|
<nav class="sitenav" aria-labelledby="main-menu">
|
||||||
|
<FluentNavMenu Id="main-menu" Collapsible="true" Width="250" Title="Navigation menu" @bind-Expanded="expanded" CustomToggle="true">
|
||||||
|
<FluentNavLink Href="/" Match="NavLinkMatch.All" Icon="@(new Size20.Home())" IconColor="Color.Accent">Home</FluentNavLink>
|
||||||
|
<FluentNavLink Href="transactions" Icon="@(new Size20.NumberSymbolSquare())" IconColor="Color.Accent">Transaktionen</FluentNavLink>
|
||||||
|
<FluentNavLink Href="banktransactions" Icon="@(new Size20.BuildingBank())" IconColor="Color.Accent">Bank Transaktionen</FluentNavLink>
|
||||||
|
<FluentNavGroup Title="Stammdaten" Icon="@(new Size32.BookDatabase())">
|
||||||
|
<FluentNavLink Href="bookingrules" Icon="@(new Size20.Check())" IconColor="Color.Accent">Buchungsregeln</FluentNavLink>
|
||||||
|
<FluentNavLink Href="bookingtexts" Icon="@(new Size32.Text())" IconColor="Color.Accent">Buchungstexte</FluentNavLink>
|
||||||
|
<FluentNavLink Href="Classes" Icon="@(new Icons.Filled.Size20.Class())" IconColor="Color.Accent">Klassen</FluentNavLink>
|
||||||
|
<FluentNavLink Href="Accounts" Icon="@(new Size20.PersonAccounts())" IconColor="Color.Accent">Konten</FluentNavLink>
|
||||||
|
</FluentNavGroup>
|
||||||
|
<FluentNavGroup Title="Charts" Icon="@(new Size32.ChartMultiple())">
|
||||||
|
<FluentNavLink Href="spendings" Icon="@(new Size32.DataPie())" IconColor="Color.Accent">Ausgaben</FluentNavLink>
|
||||||
|
<FluentNavLink Href="SpendingsOverTime" Icon="@(new Size32.DataArea())" IconColor="Color.Accent">Zeitverlauf</FluentNavLink>
|
||||||
|
<FluentNavLink Href="Assets" Icon="@(new Size32.DataPie())" IconColor="Color.Accent">Vermögen</FluentNavLink>
|
||||||
|
</FluentNavGroup>
|
||||||
|
<FluentNavGroup Title="Reports" Icon="@(new Size32.DocumentPrint())">
|
||||||
|
<FluentNavLink Href="ProfitLossReport" Icon="@(new Size32.DataTrending())" IconColor="Color.Accent">Erfolgsrechnung</FluentNavLink>
|
||||||
|
<FluentNavLink Href="DetailReport" Icon="@(new Size32.Feed())" IconColor="Color.Accent">Detailaufstellung</FluentNavLink>
|
||||||
|
<FluentNavLink Href="BalanceReport" Icon="@(new Size32.Feed())" IconColor="Color.Accent">Bilanz</FluentNavLink>
|
||||||
|
<FluentNavLink Href="BalanceSheetReport" Icon="@(new Size32.Receipt())" IconColor="Color.Accent">Jahresabschluss</FluentNavLink>
|
||||||
|
</FluentNavGroup>
|
||||||
|
</FluentNavMenu>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool expanded = true;
|
||||||
|
}
|
||||||
105
Schaad.Accounting.UI/Components/Layout/NavMenu.razor.css
Normal file
105
Schaad.Accounting.UI/Components/Layout/NavMenu.razor.css
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
.navbar-toggler {
|
||||||
|
appearance: none;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 3.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
color: white;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
right: 1rem;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-toggler:checked {
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
min-height: 3.5rem;
|
||||||
|
background-color: rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
top: -1px;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-house-door-fill-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-plus-square-fill-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-list-nested-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:first-of-type {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:last-of-type {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep .nav-link {
|
||||||
|
color: #d7d7d7;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 3rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 3rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep a.active {
|
||||||
|
background-color: rgba(255,255,255,0.37);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep .nav-link:hover {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-scrollable {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-toggler:checked ~ .nav-scrollable {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 641px) {
|
||||||
|
.navbar-toggler {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-scrollable {
|
||||||
|
/* Never collapse the sidebar for wide screens */
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
/* Allow sidebar to scroll for tall menus */
|
||||||
|
height: calc(100vh - 3.5rem);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Schaad.Accounting.UI/Components/Pages/Accounts.razor
Normal file
30
Schaad.Accounting.UI/Components/Pages/Accounts.razor
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
@page "/Accounts"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Konten</PageTitle>
|
||||||
|
|
||||||
|
<FluentButton IconStart="@(new Icons.Regular.Size16.Add())" Appearance="Appearance.Neutral" OnClick="AddAsync">Neues Konto hinzufügen</FluentButton>
|
||||||
|
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||||
|
|
||||||
|
<FluentDataGrid Items="@accountQueryable" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||||
|
<PropertyColumn Property="@(p => p.Class)" Sortable="true" Width="100px" />
|
||||||
|
<PropertyColumn Property="@(p => p.SubClass)" Sortable="true" Width="140px" />
|
||||||
|
<PropertyColumn Property="@(p => p.Number)" Sortable="true" IsDefaultSortColumn="true" Width="140px" />
|
||||||
|
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
|
||||||
|
<PropertyColumn Property="@(p => p.Currency)" />
|
||||||
|
<PropertyColumn Property="@(p => p.BankAccountNumber)" />
|
||||||
|
<PropertyColumn Property="@(p => p.StartBalance)" Format="C2"/>
|
||||||
|
<PropertyColumn Property="@(p => p.LastBankBalance)" Format="C2" />
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Delete())" Appearance="Appearance.Neutral" OnClick="@(() => DeleteAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Edit())" Appearance="Appearance.Neutral" OnClick="@(() => EditAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
</FluentDataGrid>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private RenderFragment template = @<span />;
|
||||||
|
PaginationState pagination = new PaginationState() { ItemsPerPage = 20 };
|
||||||
|
}
|
||||||
72
Schaad.Accounting.UI/Components/Pages/Accounts.razor.cs
Normal file
72
Schaad.Accounting.UI/Components/Pages/Accounts.razor.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
using Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages;
|
||||||
|
|
||||||
|
public partial class Accounts : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IAccountRepository accountRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService dialogService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IQueryable<Account>? accountQueryable;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
accountQueryable = accountRepository.GetAccountList().AsQueryable();
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddAsync()
|
||||||
|
{
|
||||||
|
var data = new Account();
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<AccountDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Konto",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
accountQueryable = accountRepository.GetAccountList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EditAsync(string id)
|
||||||
|
{
|
||||||
|
var data = accountRepository.GetAccount(id);
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<AccountDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Konto",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
accountQueryable = accountRepository.GetAccountList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync(string id)
|
||||||
|
{
|
||||||
|
var account = accountRepository.GetAccount(id);
|
||||||
|
var dialog = await dialogService.ShowConfirmationAsync($"Konto '{account.Name}' wirklich löschen?", "Ja", "Nein", "Konto löschen");
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled)
|
||||||
|
{
|
||||||
|
accountRepository.DeleteAccount(id);
|
||||||
|
accountQueryable = accountRepository.GetAccountList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Schaad.Accounting.UI/Components/Pages/BankTransactions.razor
Normal file
19
Schaad.Accounting.UI/Components/Pages/BankTransactions.razor
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
@page "/banktransactions"
|
||||||
|
<PageTitle>BankTransactions</PageTitle>
|
||||||
|
|
||||||
|
<FluentPaginator State="@pagination" />
|
||||||
|
|
||||||
|
<div id="datagrid-container">
|
||||||
|
|
||||||
|
<FluentDataGrid Items="@TransactionList" Pagination="@pagination" RowSize="@rowSize" AutoItemsPerPage="true" Style="overflow-y:hidden;">
|
||||||
|
<PropertyColumn Title="Buchungsdatum" Width="100" Property="@(p => p.BookingDate)" Format="dd.MM.yyyy" Sortable="true" IsDefaultSortColumn="true" InitialSortDirection="SortDirection.Descending" />
|
||||||
|
<PropertyColumn Title="Valutadatum" Width="100" Property="@(p => p.ValueDate)" Format="dd.MM.yyyy" Sortable="true" />
|
||||||
|
<PropertyColumn Title="Text" Property="@(p => p.Text)" />
|
||||||
|
<PropertyColumn Title="Betrag" Width="100" Property="@(p => p.Value)" Format="C2" Align="Align.End"/>
|
||||||
|
</FluentDataGrid>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
DataGridRowSize rowSize = DataGridRowSize.Small;
|
||||||
|
PaginationState pagination = new PaginationState() { ItemsPerPage = 10 };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages;
|
||||||
|
|
||||||
|
public partial class BankTransactions : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IBankTransactionRepository bankTransactionRepository { get; set; } = null!;
|
||||||
|
private IQueryable<BankTransaction>? TransactionList;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
TransactionList = bankTransactionRepository.GetBankTransactionList().AsQueryable();
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#datagrid-container {
|
||||||
|
height: calc(100% - 3rem);
|
||||||
|
min-height: 8rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
min-height: 32rem;
|
||||||
|
max-height: 80dvh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-section-content {
|
||||||
|
height: calc(100% - 10rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-section-example {
|
||||||
|
min-height: 135px !important;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
fluent-tabs {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tab-example-autoitemsperpage-panel {
|
||||||
|
height: 100% !important;
|
||||||
|
max-height: calc(100% - 2rem) !important;
|
||||||
|
}
|
||||||
26
Schaad.Accounting.UI/Components/Pages/BookingRules.razor
Normal file
26
Schaad.Accounting.UI/Components/Pages/BookingRules.razor
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
@page "/bookingrules"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Buchungsregeln</PageTitle>
|
||||||
|
|
||||||
|
<FluentButton IconStart="@(new Icons.Regular.Size16.Add())" Appearance="Appearance.Neutral" OnClick="AddAsync">Neue Regel hinzufügen</FluentButton>
|
||||||
|
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||||
|
|
||||||
|
<FluentDataGrid Items="@bookingRulesQueryable" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||||
|
<PropertyColumn Property="@(p => p.LookupText)" Sortable="true" IsDefaultSortColumn="true" />
|
||||||
|
<PropertyColumn Property="@(p => p.LookupValue)" Format="C2" Align="Align.End"/>
|
||||||
|
<PropertyColumn Property="@(p => p.BookingText)" />
|
||||||
|
<PropertyColumn Property="@(p => p.Account)" />
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Delete())" Appearance="Appearance.Neutral" OnClick="@(() => DeleteAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Edit())" Appearance="Appearance.Neutral" OnClick="@(() => EditAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
</FluentDataGrid>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private RenderFragment template = @<span />;
|
||||||
|
PaginationState pagination = new PaginationState() { ItemsPerPage = 20 };
|
||||||
|
}
|
||||||
76
Schaad.Accounting.UI/Components/Pages/BookingRules.razor.cs
Normal file
76
Schaad.Accounting.UI/Components/Pages/BookingRules.razor.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
using Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages;
|
||||||
|
|
||||||
|
public partial class BookingRules : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IBookingRuleRepository bookingRuleRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService dialogService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IQueryable<BookingRuleDataset>? bookingRulesQueryable;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
bookingRulesQueryable = viewService.GetBookingRuleViewList().AsQueryable();
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddAsync()
|
||||||
|
{
|
||||||
|
var data = new BookingRule();
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<BookingRuleDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Buchungsregel",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
bookingRulesQueryable = viewService.GetBookingRuleViewList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EditAsync(string id)
|
||||||
|
{
|
||||||
|
var data = bookingRuleRepository.GetBookingRule(id);
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<BookingRuleDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Buchungsregel",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
bookingRulesQueryable = viewService.GetBookingRuleViewList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync(string id)
|
||||||
|
{
|
||||||
|
var bookingRule = bookingRuleRepository.GetBookingRule(id);
|
||||||
|
var dialog = await dialogService.ShowConfirmationAsync($"Regel '{bookingRule.LookupText}' wirklich löschen?", "Ja", "Nein", "Buchungsregel löschen");
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled)
|
||||||
|
{
|
||||||
|
bookingRuleRepository.DeleteBookingRule(id);
|
||||||
|
bookingRulesQueryable = viewService.GetBookingRuleViewList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Schaad.Accounting.UI/Components/Pages/BookingTexts.razor
Normal file
23
Schaad.Accounting.UI/Components/Pages/BookingTexts.razor
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
@page "/bookingtexts"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Buchungstexte</PageTitle>
|
||||||
|
|
||||||
|
<FluentButton IconStart="@(new Icons.Regular.Size16.Add())" Appearance="Appearance.Neutral" OnClick="AddAsync">Neuer Text hinzufügen</FluentButton>
|
||||||
|
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||||
|
|
||||||
|
<FluentDataGrid Items="@bookingTextQueryable" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||||
|
<PropertyColumn Property="@(p => p.Text)" Sortable="true" IsDefaultSortColumn="true" />
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Delete())" Appearance="Appearance.Neutral" OnClick="@(() => DeleteAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Edit())" Appearance="Appearance.Neutral" OnClick="@(() => EditAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
</FluentDataGrid>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private RenderFragment template = @<span />;
|
||||||
|
PaginationState pagination = new PaginationState() { ItemsPerPage = 20 };
|
||||||
|
}
|
||||||
72
Schaad.Accounting.UI/Components/Pages/BookingTexts.razor.cs
Normal file
72
Schaad.Accounting.UI/Components/Pages/BookingTexts.razor.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
using Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages;
|
||||||
|
|
||||||
|
public partial class BookingTexts : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IBookingTextRepository bookingTextRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService dialogService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IQueryable<BookingText>? bookingTextQueryable;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
bookingTextQueryable = bookingTextRepository.GetBookingTextList().AsQueryable();
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddAsync()
|
||||||
|
{
|
||||||
|
var data = new BookingText();
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<BookingTextDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Add Buchungstext",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
bookingTextQueryable = bookingTextRepository.GetBookingTextList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EditAsync(string id)
|
||||||
|
{
|
||||||
|
var data = bookingTextRepository.GetBookingText(id);
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<BookingTextDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Edit Buchungstext",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
bookingTextQueryable = bookingTextRepository.GetBookingTextList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync(string id)
|
||||||
|
{
|
||||||
|
var bookingText = bookingTextRepository.GetBookingText(id);
|
||||||
|
var dialog = await dialogService.ShowConfirmationAsync($"Buchungstext '{bookingText.Text}' wirklich löschen?", "Ja", "Nein", "Buchungstext löschen");
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled)
|
||||||
|
{
|
||||||
|
bookingTextRepository.DeleteBookingText(id);
|
||||||
|
bookingTextQueryable = bookingTextRepository.GetBookingTextList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
@page "/Assets"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Ausgaben</PageTitle>
|
||||||
|
|
||||||
|
<PlotlyChart style="height: 700px; min-height: 500px" @bind-Config="config" @bind-Layout="layout" @bind-Data="data" @ref="chart" />
|
||||||
55
Schaad.Accounting.UI/Components/Pages/Charts/Assets.razor.cs
Normal file
55
Schaad.Accounting.UI/Components/Pages/Charts/Assets.razor.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Plotly.Blazor;
|
||||||
|
using Plotly.Blazor.Traces;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Charts;
|
||||||
|
|
||||||
|
public partial class Assets : ComponentBase
|
||||||
|
{
|
||||||
|
private PlotlyChart chart = null!;
|
||||||
|
private Config config = null!;
|
||||||
|
private Plotly.Blazor.Layout layout = null!;
|
||||||
|
private IList<ITrace> data = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
config = new Config
|
||||||
|
{
|
||||||
|
Responsive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
layout = new Plotly.Blazor.Layout
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
var accounts = viewService.GetAccountViewList().Where(a => a.Class == ClassIds.Activa);
|
||||||
|
var values = new List<object>();
|
||||||
|
var labels = new List<object>();
|
||||||
|
var ids = new List<object>();
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
if (account.BalanceCHF > 0)
|
||||||
|
{
|
||||||
|
values.Add(account.BalanceCHF);
|
||||||
|
labels.Add(account.Name);
|
||||||
|
ids.Add(account.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = new List<ITrace>
|
||||||
|
{
|
||||||
|
new Pie
|
||||||
|
{
|
||||||
|
Values = values,
|
||||||
|
Labels = labels,
|
||||||
|
Ids = ids
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
@page "/spendings"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Ausgaben</PageTitle>
|
||||||
|
|
||||||
|
<PlotlyChart style="height: 700px; min-height: 500px" @bind-Config="config" @bind-Layout="layout" @bind-Data="data" @ref="chart" />
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Plotly.Blazor;
|
||||||
|
using Plotly.Blazor.Traces;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Charts;
|
||||||
|
|
||||||
|
public partial class Spendings : ComponentBase
|
||||||
|
{
|
||||||
|
private PlotlyChart chart = null!;
|
||||||
|
private Config config = null!;
|
||||||
|
private Plotly.Blazor.Layout layout = null!;
|
||||||
|
private IList<ITrace> data = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
config = new Config
|
||||||
|
{
|
||||||
|
Responsive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
layout = new Plotly.Blazor.Layout
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
var accounts = viewService.GetAccountViewList().Where(a => a.Class == ClassIds.Expenses);
|
||||||
|
var values = new List<object>();
|
||||||
|
var labels = new List<object>();
|
||||||
|
foreach (var grp in accounts.GroupBy(a => a.SubClass).Select(a => new {Key = a.Key, List = a.ToList()}))
|
||||||
|
{
|
||||||
|
if (grp.List.Sum(g => g.Balance) != 0)
|
||||||
|
{
|
||||||
|
values.Add(grp.List.Sum(g => g.Balance));
|
||||||
|
labels.Add(grp.List.First().SubClassName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = new List<ITrace>
|
||||||
|
{
|
||||||
|
new Pie
|
||||||
|
{
|
||||||
|
Values =values,
|
||||||
|
Labels = labels
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
@page "/SpendingsOverTime"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Ausgaben im Verlauf</PageTitle>
|
||||||
|
|
||||||
|
<PlotlyChart style="height: 700px; min-height: 500px" @bind-Config="config" @bind-Layout="layout" @bind-Data="data" @ref="chart" />
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Plotly.Blazor;
|
||||||
|
using Plotly.Blazor.LayoutLib;
|
||||||
|
using Plotly.Blazor.Traces;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Charts;
|
||||||
|
|
||||||
|
public partial class SpendingsOverTime : ComponentBase
|
||||||
|
{
|
||||||
|
private PlotlyChart chart = null!;
|
||||||
|
private Config config = null!;
|
||||||
|
private Plotly.Blazor.Layout layout = null!;
|
||||||
|
private IList<ITrace> data = new List<ITrace>();
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IChartService chartService { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
config = new Config
|
||||||
|
{
|
||||||
|
Responsive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
layout = new Plotly.Blazor.Layout
|
||||||
|
{
|
||||||
|
Title = new Title { Text = "Ausgaben im Verlauf" },
|
||||||
|
BarMode = BarModeEnum.Stack,
|
||||||
|
YAxis = new List<YAxis>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Title = new Plotly.Blazor.LayoutLib.YAxisLib.Title { Text = "CHF" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
XAxis = new List<XAxis>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Type = Plotly.Blazor.LayoutLib.XAxisLib.TypeEnum.Date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var dataSeries = chartService.GetExpensesPerMonth();
|
||||||
|
foreach (var dataSerie in dataSeries)
|
||||||
|
{
|
||||||
|
data.Add(new Bar
|
||||||
|
{
|
||||||
|
Name = dataSerie.Name,
|
||||||
|
X = dataSerie.X.Select(d => (object)d).ToList(),
|
||||||
|
Y = dataSerie.Y.Select(d => (object)d).ToList(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Schaad.Accounting.UI/Components/Pages/Classes.razor
Normal file
24
Schaad.Accounting.UI/Components/Pages/Classes.razor
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
@page "/Classes"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Klassen</PageTitle>
|
||||||
|
|
||||||
|
<FluentButton IconStart="@(new Icons.Regular.Size16.Add())" Appearance="Appearance.Neutral" OnClick="AddAsync">Neue Klasse hinzufügen</FluentButton>
|
||||||
|
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||||
|
|
||||||
|
<FluentDataGrid Items="@subclassQueryable" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||||
|
<PropertyColumn Property="@(p => p.Number)" Sortable="true" IsDefaultSortColumn="true" Width="200px" />
|
||||||
|
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Delete())" Appearance="Appearance.Neutral" OnClick="@(() => DeleteAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Align="Align.Center" Width="50px">
|
||||||
|
<FluentButton IconEnd="@(new Icons.Regular.Size16.Edit())" Appearance="Appearance.Neutral" OnClick="@(() => EditAsync(context.Id))" />
|
||||||
|
</TemplateColumn>
|
||||||
|
</FluentDataGrid>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private RenderFragment template = @<span />;
|
||||||
|
PaginationState pagination = new PaginationState() { ItemsPerPage = 20 };
|
||||||
|
}
|
||||||
72
Schaad.Accounting.UI/Components/Pages/Classes.razor.cs
Normal file
72
Schaad.Accounting.UI/Components/Pages/Classes.razor.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
using Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages;
|
||||||
|
|
||||||
|
public partial class Classes : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private ISubclassRepository subclassRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService dialogService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IQueryable<SubClass>? subclassQueryable;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
subclassQueryable = subclassRepository.GetSubClassList().AsQueryable();
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddAsync()
|
||||||
|
{
|
||||||
|
var data = new SubClass();
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<ClassDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Klasse",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
subclassQueryable = subclassRepository.GetSubClassList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EditAsync(string id)
|
||||||
|
{
|
||||||
|
var data = subclassRepository.GetSubClass(id);
|
||||||
|
var dialog = await dialogService.ShowDialogAsync<ClassDialog>(data, new DialogParameters()
|
||||||
|
{
|
||||||
|
Height = "500px",
|
||||||
|
Title = "Klasse",
|
||||||
|
PreventDismissOnOverlayClick = true,
|
||||||
|
PreventScroll = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled && result.Data != null)
|
||||||
|
{
|
||||||
|
subclassQueryable = subclassRepository.GetSubClassList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync(string id)
|
||||||
|
{
|
||||||
|
var subclass = subclassRepository.GetSubClass(id);
|
||||||
|
var dialog = await dialogService.ShowConfirmationAsync($"Klasse '{subclass.Name}' wirklich löschen?", "Ja", "Nein", "Klasse löschen");
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled)
|
||||||
|
{
|
||||||
|
subclassRepository.DeleteSubClass(id);
|
||||||
|
subclassQueryable = subclassRepository.GetSubClassList().AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
@implements IDialogContentComponent<Schaad.Accounting.Models.Account>
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<FluentDialogHeader ShowDismiss="true">
|
||||||
|
<FluentStack VerticalAlignment="VerticalAlignment.Center">
|
||||||
|
<FluentIcon Value="@(new Icons.Regular.Size24.WindowApps())" />
|
||||||
|
<FluentLabel Typo="Typography.PaneHeader">
|
||||||
|
@Dialog.Instance.Parameters.Title
|
||||||
|
</FluentLabel>
|
||||||
|
</FluentStack>
|
||||||
|
</FluentDialogHeader>
|
||||||
|
|
||||||
|
<FluentDialogBody>
|
||||||
|
<EditForm EditContext="@editContext">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
<FluentNumberField @bind-Value="@Content.Number" Label="Nummer" Style="width: 300px;"/>
|
||||||
|
<FluentTextField @bind-Value="@Content.Name" Label="Name" Style="width: 300px;"/>
|
||||||
|
<FluentTextField @bind-Value="@Content.Currency" Label="Währung" Style="width: 300px;"/>
|
||||||
|
<FluentTextField @bind-Value="@Content.BankAccountNumber" Label="Bankkonto" Style="width: 300px;"/>
|
||||||
|
<FluentNumberField @bind-Value="@Content.StartBalance" Label="Startsaldo" Style="width: 300px;"/>
|
||||||
|
<div>
|
||||||
|
<FluentValidationSummary />
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</FluentDialogBody>
|
||||||
|
|
||||||
|
<FluentDialogFooter>
|
||||||
|
<FluentButton Appearance="Appearance.Neutral"
|
||||||
|
OnClick="@CancelAsync">
|
||||||
|
Abbrechen
|
||||||
|
</FluentButton>
|
||||||
|
<FluentButton Appearance="Appearance.Accent"
|
||||||
|
OnClick="@SaveAsync">
|
||||||
|
Speichern
|
||||||
|
</FluentButton>
|
||||||
|
</FluentDialogFooter>
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
public partial class AccountDialog : ComponentBase
|
||||||
|
{
|
||||||
|
private EditContext editContext = null!;
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
public FluentDialog Dialog { get; set; } = null!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Account Content { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IAccountRepository accountRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
editContext = new EditContext(Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveAsync()
|
||||||
|
{
|
||||||
|
if (editContext.Validate())
|
||||||
|
{
|
||||||
|
accountRepository.SaveAccount(Content);
|
||||||
|
await Dialog.CloseAsync(Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CancelAsync()
|
||||||
|
{
|
||||||
|
await Dialog.CancelAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
@using Schaad.Accounting.Models
|
||||||
|
@implements IDialogContentComponent<Schaad.Accounting.Models.BookingRule>
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<FluentDialogHeader ShowDismiss="true">
|
||||||
|
<FluentStack VerticalAlignment="VerticalAlignment.Center">
|
||||||
|
<FluentIcon Value="@(new Icons.Regular.Size24.WindowApps())" />
|
||||||
|
<FluentLabel Typo="Typography.PaneHeader">
|
||||||
|
@Dialog.Instance.Parameters.Title
|
||||||
|
</FluentLabel>
|
||||||
|
</FluentStack>
|
||||||
|
</FluentDialogHeader>
|
||||||
|
|
||||||
|
<FluentDialogBody>
|
||||||
|
<EditForm EditContext="@editContext">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
<FluentTextField @bind-Value="@Content.LookupText" Label="Suchtext" Style="width: 300px;"/>
|
||||||
|
<FluentNumberField @bind-Value="@Content.LookupValue" Label="Suchbetrag" Style="width: 300px;"/>
|
||||||
|
<FluentTextField @bind-Value="@Content.BookingText" Label="Buchungstext" Style="width: 300px;" />
|
||||||
|
<FluentSelect TOption="Account"
|
||||||
|
Label="Konto"
|
||||||
|
Items="@accounts"
|
||||||
|
Width="300px"
|
||||||
|
Height="250px"
|
||||||
|
Required="true"
|
||||||
|
Placeholder="Konto auswählen..."
|
||||||
|
OptionValue="@(p => p.Id)"
|
||||||
|
OptionText="@(p => p.Number + " / " + p.Name)"
|
||||||
|
@bind-Value="@Content.AccountId" />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FluentValidationSummary />
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</FluentDialogBody>
|
||||||
|
|
||||||
|
<FluentDialogFooter>
|
||||||
|
<FluentButton Appearance="Appearance.Neutral"
|
||||||
|
OnClick="@CancelAsync">
|
||||||
|
Abbrechen
|
||||||
|
</FluentButton>
|
||||||
|
<FluentButton Appearance="Appearance.Accent"
|
||||||
|
OnClick="@SaveAsync">
|
||||||
|
Speichern
|
||||||
|
</FluentButton>
|
||||||
|
</FluentDialogFooter>
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
public partial class BookingRuleDialog : ComponentBase
|
||||||
|
{
|
||||||
|
private EditContext editContext = null!;
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
public FluentDialog Dialog { get; set; } = null!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public BookingRule Content { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IBookingRuleRepository bookingRuleRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IReadOnlyList<Account> accounts = [];
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
editContext = new EditContext(Content);
|
||||||
|
accounts = viewService.GetAccountViewList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveAsync()
|
||||||
|
{
|
||||||
|
if (editContext.Validate())
|
||||||
|
{
|
||||||
|
bookingRuleRepository.SaveBookingRule(Content);
|
||||||
|
await Dialog.CloseAsync(Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CancelAsync()
|
||||||
|
{
|
||||||
|
await Dialog.CancelAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
@implements IDialogContentComponent<Schaad.Accounting.Models.BookingText>
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<FluentDialogHeader ShowDismiss="true">
|
||||||
|
<FluentStack VerticalAlignment="VerticalAlignment.Center">
|
||||||
|
<FluentIcon Value="@(new Icons.Regular.Size24.WindowApps())" />
|
||||||
|
<FluentLabel Typo="Typography.PaneHeader">
|
||||||
|
@Dialog.Instance.Parameters.Title
|
||||||
|
</FluentLabel>
|
||||||
|
</FluentStack>
|
||||||
|
</FluentDialogHeader>
|
||||||
|
|
||||||
|
<FluentDialogBody>
|
||||||
|
<EditForm EditContext="@editContext">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
<FluentTextField @bind-Value="@Content.Text" Label="Buchungstext" />
|
||||||
|
<div>
|
||||||
|
<FluentValidationSummary />
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</FluentDialogBody>
|
||||||
|
|
||||||
|
<FluentDialogFooter>
|
||||||
|
<FluentButton Appearance="Appearance.Neutral"
|
||||||
|
OnClick="@CancelAsync">
|
||||||
|
Abbrechen
|
||||||
|
</FluentButton>
|
||||||
|
<FluentButton Appearance="Appearance.Accent"
|
||||||
|
OnClick="@SaveAsync">
|
||||||
|
Speichern
|
||||||
|
</FluentButton>
|
||||||
|
</FluentDialogFooter>
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
public partial class BookingTextDialog : ComponentBase
|
||||||
|
{
|
||||||
|
private EditContext editContext = null!;
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
public FluentDialog Dialog { get; set; } = null!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public BookingText Content { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IBookingTextRepository bookingTextRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
editContext = new EditContext(Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveAsync()
|
||||||
|
{
|
||||||
|
if (editContext.Validate())
|
||||||
|
{
|
||||||
|
bookingTextRepository.SaveBookingText(Content);
|
||||||
|
await Dialog.CloseAsync(Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CancelAsync()
|
||||||
|
{
|
||||||
|
await Dialog.CancelAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
@implements IDialogContentComponent<Schaad.Accounting.Models.SubClass>
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<FluentDialogHeader ShowDismiss="true">
|
||||||
|
<FluentStack VerticalAlignment="VerticalAlignment.Center">
|
||||||
|
<FluentIcon Value="@(new Icons.Regular.Size24.WindowApps())" />
|
||||||
|
<FluentLabel Typo="Typography.PaneHeader">
|
||||||
|
@Dialog.Instance.Parameters.Title
|
||||||
|
</FluentLabel>
|
||||||
|
</FluentStack>
|
||||||
|
</FluentDialogHeader>
|
||||||
|
|
||||||
|
<FluentDialogBody>
|
||||||
|
<EditForm EditContext="@editContext">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
<FluentNumberField @bind-Value="@Content.Number" Label="Nummer" Style="width: 300px;"/>
|
||||||
|
<FluentTextField @bind-Value="@Content.Name" Label="Name" Style="width: 300px;"/>
|
||||||
|
<div>
|
||||||
|
<FluentValidationSummary />
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</FluentDialogBody>
|
||||||
|
|
||||||
|
<FluentDialogFooter>
|
||||||
|
<FluentButton Appearance="Appearance.Neutral"
|
||||||
|
OnClick="@CancelAsync">
|
||||||
|
Abbrechen
|
||||||
|
</FluentButton>
|
||||||
|
<FluentButton Appearance="Appearance.Accent"
|
||||||
|
OnClick="@SaveAsync">
|
||||||
|
Speichern
|
||||||
|
</FluentButton>
|
||||||
|
</FluentDialogFooter>
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||||
|
|
||||||
|
public partial class ClassDialog : ComponentBase
|
||||||
|
{
|
||||||
|
private EditContext editContext = null!;
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
public FluentDialog Dialog { get; set; } = null!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public SubClass Content { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ISubclassRepository bookingRuleRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
editContext = new EditContext(Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveAsync()
|
||||||
|
{
|
||||||
|
if (editContext.Validate())
|
||||||
|
{
|
||||||
|
bookingRuleRepository.SaveSubClass(Content);
|
||||||
|
await Dialog.CloseAsync(Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CancelAsync()
|
||||||
|
{
|
||||||
|
await Dialog.CancelAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Schaad.Accounting.UI/Components/Pages/Error.razor
Normal file
36
Schaad.Accounting.UI/Components/Pages/Error.razor
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
@page "/Error"
|
||||||
|
@using System.Diagnostics
|
||||||
|
|
||||||
|
<PageTitle>Error</PageTitle>
|
||||||
|
|
||||||
|
<h1 class="text-danger">Error.</h1>
|
||||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
|
|
||||||
|
@if (ShowRequestId)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<h3>Development Mode</h3>
|
||||||
|
<p>
|
||||||
|
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||||
|
It can result in displaying sensitive information from exceptions to end users.
|
||||||
|
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||||
|
and restarting the app.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@code{
|
||||||
|
[CascadingParameter]
|
||||||
|
private HttpContext? HttpContext { get; set; }
|
||||||
|
|
||||||
|
private string? RequestId { get; set; }
|
||||||
|
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
|
|
||||||
|
protected override void OnInitialized() =>
|
||||||
|
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||||
|
}
|
||||||
7
Schaad.Accounting.UI/Components/Pages/Home.razor
Normal file
7
Schaad.Accounting.UI/Components/Pages/Home.razor
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
@page "/"
|
||||||
|
<h3>Home</h3>
|
||||||
|
|
||||||
|
@foreach (var bankTransaction in bankTransactions)
|
||||||
|
{
|
||||||
|
<p>@bankTransaction.BankAccountNumber</p>
|
||||||
|
}
|
||||||
19
Schaad.Accounting.UI/Components/Pages/Home.razor.cs
Normal file
19
Schaad.Accounting.UI/Components/Pages/Home.razor.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
using Schaad.Accounting.Models;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages;
|
||||||
|
|
||||||
|
public partial class Home : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
private List<BankTransaction> bankTransactions = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
bankTransactions = viewService.GetOpenBankTransactionList();
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
@page "/BalanceReport"
|
||||||
|
|
||||||
|
<h3>@header</h3>
|
||||||
|
<a href="javascript:window.print()" class="hidePrint">Print</a>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<table class="report">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Konto</th>
|
||||||
|
<th>Bezeichnung</th>
|
||||||
|
<th class="right">Aktiven</th>
|
||||||
|
<th class="right">Passiven</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="4"><hr /></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="header">Aktiven</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach (var account in balance.ActivaAccountList.OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.BalanceCHF == 0) { continue; }
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@account.Number</td>
|
||||||
|
<td>@account.Name</td>
|
||||||
|
<td class="right">@Math.Abs(account.BalanceCHF).ToFormattedString()</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Total Aktiven</td>
|
||||||
|
<td class="right total">CHF @balance.TotalActivaCHF.ToFormattedString()</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="header">Passiven</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach (var account in balance.PassivaAccountList.OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.BalanceCHF == 0) { continue; }
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@account.Number</td>
|
||||||
|
<td>@account.Name</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="right">@Math.Abs(account.BalanceCHF).ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Total Passiven</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="right total">@balance.TotalPassivaCHF.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Total</td>
|
||||||
|
<td class="right">@balance.TotalActivaCHF.ToFormattedString()</td>
|
||||||
|
<td class="right">@balance.TotalPassivaCHF.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Eigenkapital</td>
|
||||||
|
<td class="right sum"></td>
|
||||||
|
<td class="right sum">@win.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Bilanzsumme</td>
|
||||||
|
<td class="right total sum">CHF @balance.TotalActivaCHF.ToFormattedString()</td>
|
||||||
|
<td class="right total sum">CHF @((balance.TotalPassivaCHF + win).ToFormattedString())</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" style="height: 30px"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="footer">@footer</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Datasets.Reports;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Reports;
|
||||||
|
|
||||||
|
public partial class BalanceReport : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ISettingsService settingsService { get; set; } = null!;
|
||||||
|
|
||||||
|
private BalanceDataset balance = null!;
|
||||||
|
private decimal win;
|
||||||
|
private string header = null!;
|
||||||
|
private string footer = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
balance = viewService.GetBalanceView();
|
||||||
|
win = balance.TotalActivaCHF - balance.TotalPassivaCHF;
|
||||||
|
(header, footer) = Report.GetViewDataTitleAndFooter("Bilanz", settingsService);
|
||||||
|
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
@page "/BalanceSheetReport"
|
||||||
|
|
||||||
|
<h3>@header</h3>
|
||||||
|
<a href="javascript:window.print()" class="hidePrint">Print</a>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<table class="report">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header">Einnahmen</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach (var account in balanceSheet.ActivaAccountList.OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.StartBalanceCHF == 0) { continue; }
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@account.Number</td>
|
||||||
|
<td>Vortrag von @(balanceSheet.Year - 1) / @account.Name</td>
|
||||||
|
<td class="right">@Math.Abs(account.StartBalanceCHF).ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
@foreach (var account in balanceSheet.IncomeAccountList.OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.BalanceCHF == 0) { continue; }
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@account.Number</td>
|
||||||
|
<td>@account.Name</td>
|
||||||
|
<td class="right">@Math.Abs(account.BalanceCHF).ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header">Ausgaben</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach (var account in balanceSheet.ExpensesAccountList.OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.BalanceCHF == 0) { continue; }
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@account.Number</td>
|
||||||
|
<td>@account.Name</td>
|
||||||
|
<td class="right">@account.BalanceCHF.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">Total Einnahmen</td>
|
||||||
|
<td class="right">@balanceSheet.ProfitCHF.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">Total Ausgaben</td>
|
||||||
|
<td class="right sum">-@balanceSheet.LossCHF.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Übertrag @(balanceSheet.Year + 1)</td>
|
||||||
|
<td class="right total sum">CHF @win.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" style="height: 30px"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="footer">@footer</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Datasets.Reports;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Reports;
|
||||||
|
|
||||||
|
public partial class BalanceSheetReport : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ISettingsService settingsService { get; set; } = null!;
|
||||||
|
|
||||||
|
private BalanceSheetDataset balanceSheet = null!;
|
||||||
|
private decimal win;
|
||||||
|
private string header = null!;
|
||||||
|
private string footer = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
balanceSheet = viewService.GetBalanceSheetView(settingsService.GetYear());
|
||||||
|
win = balanceSheet.ProfitCHF - balanceSheet.LossCHF;
|
||||||
|
(header, footer) = Report.GetViewDataTitleAndFooter("Jahresabschluss", settingsService);
|
||||||
|
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
112
Schaad.Accounting.UI/Components/Pages/Reports/DetailReport.razor
Normal file
112
Schaad.Accounting.UI/Components/Pages/Reports/DetailReport.razor
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
@page "/DetailReport"
|
||||||
|
|
||||||
|
<h3>@header</h3>
|
||||||
|
<a href="javascript:window.print()" class="hidePrint">Print</a>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<table class="report">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Konto</th>
|
||||||
|
<th>Bezeichnung</th>
|
||||||
|
<th class="right">Betrag</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="3"><hr /></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header">Einkünfte</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach(var account in accounts.Where(m => m.Class == 3).OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.Balance == 0) {continue;}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="header">@account.Number</td>
|
||||||
|
<td class="header">@account.Name</td>
|
||||||
|
<td class="header right">@Math.Abs(account.Balance).ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@foreach(var trx in transactions.Where(t => t.TargetAccountId == account.Id || t.OriginAccountId == account.Id).OrderBy(t => t.ValueDate))
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@trx.ValueDate.ToString("dd.MM.yyyy")</td>
|
||||||
|
<td>@trx.Text</td>
|
||||||
|
<td class="right">
|
||||||
|
@if (trx.OriginAccountId == account.Id)
|
||||||
|
{
|
||||||
|
@trx.Value.ToFormattedString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>-</span>@trx.Value.ToFormattedString();
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total right">Total Einkünfte</td>
|
||||||
|
<td class="right total">@profit.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header">Ausgaben</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach(var account in accounts.Where(m => m.Class == 4).OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.Balance == 0) {continue;}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="header">@account.Number</td>
|
||||||
|
<td class="header">@account.Name</td>
|
||||||
|
<td class="header right">@account.Balance.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@foreach(var trx in transactions.Where(t => t.TargetAccountId == account.Id || t.OriginAccountId == account.Id).OrderBy(t => t.ValueDate))
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@trx.ValueDate.ToString("dd.MM.yyyy")</td>
|
||||||
|
<td>@trx.Text</td>
|
||||||
|
<td class="right">
|
||||||
|
@if (trx.TargetAccountId == account.Id)
|
||||||
|
{
|
||||||
|
@trx.Value.ToFormattedString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>-</span>@trx.Value.ToFormattedString();
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total right">Total Ausgaben</td>
|
||||||
|
<td class="right total">@loss.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" style="height: 30px"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="footer">@footer</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Reports;
|
||||||
|
|
||||||
|
public partial class DetailReport : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ISettingsService settingsService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IReadOnlyList<AccountDataset> accounts = null!;
|
||||||
|
private IReadOnlyList<TransactionDataset> transactions = null!;
|
||||||
|
private decimal profit;
|
||||||
|
private decimal loss;
|
||||||
|
private string header = null!;
|
||||||
|
private string footer = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
accounts = viewService.GetAccountViewList();
|
||||||
|
transactions = viewService.GetTransactionViewList();
|
||||||
|
|
||||||
|
profit = Math.Abs(accounts.Where(m => m.Class == 3).Sum(m => m.Balance));
|
||||||
|
loss = Math.Abs(accounts.Where(m => m.Class == 4).Sum(m => m.Balance));
|
||||||
|
|
||||||
|
(header, footer) = Report.GetViewDataTitleAndFooter("Detailaufstellung", settingsService);
|
||||||
|
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
@page "/ProfitLossReport"
|
||||||
|
|
||||||
|
<h3>@header</h3>
|
||||||
|
<a href="javascript:window.print()" class="hidePrint">Print</a>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<table class="report">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Konto</th>
|
||||||
|
<th>Bezeichnung</th>
|
||||||
|
<th class="right">Aufwand</th>
|
||||||
|
<th class="right">Ertrag</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="4"><hr /></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="header">Einkünfte</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach(var account in accounts.Where(m => m.Class == 3).OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.Balance == 0) {continue;}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@account.Number</td>
|
||||||
|
<td>@account.Name</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="right">@Math.Abs(account.Balance).ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Total Einkünfte</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="right total">@profit.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="header">Ausgaben</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
@foreach(var account in accounts.Where(m => m.Class == 4).OrderBy(m => m.Number))
|
||||||
|
{
|
||||||
|
@if (account.Balance == 0) {continue;}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@account.Number</td>
|
||||||
|
<td>@account.Name</td>
|
||||||
|
<td class="right">@Math.Abs(account.Balance).ToFormattedString()</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Total Ausgaben</td>
|
||||||
|
<td class="right total">@loss.ToFormattedString()</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><hr /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total"></td>
|
||||||
|
<td class="right total">@loss.ToFormattedString()</td>
|
||||||
|
<td class="right total">@profit.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Gewinn/Verlust</td>
|
||||||
|
<td class="right total sum">@win.ToFormattedString()</td>
|
||||||
|
<td class="right total sum"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="total">Total</td>
|
||||||
|
<td class="right total sum">@profit.ToFormattedString()</td>
|
||||||
|
<td class="right total sum">@profit.ToFormattedString()</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" style="height: 30px"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="footer">@footer</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Reports;
|
||||||
|
|
||||||
|
public partial class ProfitLossReport : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ISettingsService settingsService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IReadOnlyList<AccountDataset> accounts = null!;
|
||||||
|
private decimal profit;
|
||||||
|
private decimal loss;
|
||||||
|
private decimal win;
|
||||||
|
private string header = null!;
|
||||||
|
private string footer = null!;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
accounts = viewService.GetAccountViewList();
|
||||||
|
profit = Math.Abs(accounts.Where(m => m.Class == 3).Sum(m => m.Balance));
|
||||||
|
loss = Math.Abs(accounts.Where(m => m.Class == 4).Sum(m => m.Balance));
|
||||||
|
win = profit-loss;
|
||||||
|
|
||||||
|
(header, footer) = Report.GetViewDataTitleAndFooter("Erfolgsrechnung", settingsService);
|
||||||
|
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Schaad.Accounting.UI/Components/Pages/Reports/Report.cs
Normal file
23
Schaad.Accounting.UI/Components/Pages/Reports/Report.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages.Reports;
|
||||||
|
|
||||||
|
public static class Report
|
||||||
|
{
|
||||||
|
public static (string header, string footer) GetViewDataTitleAndFooter(string title, ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
var header = $"{title} {settingsService.GetMandator()} {settingsService.GetYear()}";
|
||||||
|
|
||||||
|
var footer = "Stand: ";
|
||||||
|
if (DateTime.Now.Year == settingsService.GetYear())
|
||||||
|
{
|
||||||
|
footer += DateTime.Now.ToString("dd.MM.yyyy");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
footer += "31.12." + settingsService.GetYear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (header, footer);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Schaad.Accounting.UI/Components/Pages/Transactions.razor
Normal file
53
Schaad.Accounting.UI/Components/Pages/Transactions.razor
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
@page "/transactions"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Transaktionen</PageTitle>
|
||||||
|
|
||||||
|
<FluentStack Orientation="Orientation.Horizontal" Width="100%">
|
||||||
|
<FluentNavMenu Width="700">
|
||||||
|
@foreach (var gr in accounts.GroupBy(a => a.ClassName))
|
||||||
|
{
|
||||||
|
<FluentNavGroup Title="@(gr.Key)" Expanded="true">
|
||||||
|
@foreach (var account in gr)
|
||||||
|
{
|
||||||
|
<FluentNavLink OnClick="() => ShowTransactions(account.Id)" Icon="@(new Icons.Regular.Size24.CalendarAgenda())">
|
||||||
|
<FluentStack Orientation="Orientation.Horizontal">
|
||||||
|
<div>
|
||||||
|
@account.Number / @account.Name
|
||||||
|
</div>
|
||||||
|
<div style="margin-left: auto;">
|
||||||
|
<FluentBadge Appearance="Appearance.Neutral">
|
||||||
|
@account.BalanceCHF.ToString("C2")
|
||||||
|
</FluentBadge>
|
||||||
|
</div>
|
||||||
|
</FluentStack>
|
||||||
|
|
||||||
|
</FluentNavLink>
|
||||||
|
}
|
||||||
|
</FluentNavGroup>
|
||||||
|
}
|
||||||
|
</FluentNavMenu>
|
||||||
|
|
||||||
|
<FluentBodyContent>
|
||||||
|
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||||
|
|
||||||
|
<FluentDataGrid Items="@transactionList" Pagination="@pagination">
|
||||||
|
<PropertyColumn Property="@(p => p.BookingDate)" Format="dd.MM.yyyy" Sortable="true" IsDefaultSortColumn="true" InitialSortDirection="SortDirection.Descending" />
|
||||||
|
<PropertyColumn Property="@(p => p.ValueDate)" Format="dd.MM.yyyy" Sortable="true" />
|
||||||
|
<PropertyColumn Property="@(p => p.Text)" />
|
||||||
|
<TemplateColumn Title="Gegenkonto">
|
||||||
|
@GetAccountName(context)
|
||||||
|
</TemplateColumn>
|
||||||
|
<PropertyColumn Property="@(p => p.Value)" Format="C2" Align="Align.End"/>
|
||||||
|
|
||||||
|
|
||||||
|
</FluentDataGrid>
|
||||||
|
</FluentBodyContent>
|
||||||
|
|
||||||
|
</FluentStack>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private RenderFragment template = @<span />;
|
||||||
|
PaginationState pagination = new PaginationState() { ItemsPerPage = 20 };
|
||||||
|
}
|
||||||
38
Schaad.Accounting.UI/Components/Pages/Transactions.razor.cs
Normal file
38
Schaad.Accounting.UI/Components/Pages/Transactions.razor.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Schaad.Accounting.Datasets;
|
||||||
|
using Schaad.Accounting.Interfaces;
|
||||||
|
|
||||||
|
namespace Schaad.Accounting.UI.Components.Pages;
|
||||||
|
|
||||||
|
public partial class Transactions : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private IViewService viewService { get; set; } = null!;
|
||||||
|
|
||||||
|
private IReadOnlyList<AccountDataset> accounts = null!;
|
||||||
|
|
||||||
|
private IQueryable<TransactionDataset>? transactionList;
|
||||||
|
private string? selectedAccountId;
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
accounts = viewService.GetAccountViewList();
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowTransactions(string accountId)
|
||||||
|
{
|
||||||
|
selectedAccountId = accountId;
|
||||||
|
transactionList = viewService.GetTransactionViewList(accountId).AsQueryable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAccountName(TransactionDataset transaction)
|
||||||
|
{
|
||||||
|
if (selectedAccountId == transaction.OriginAccountId)
|
||||||
|
{
|
||||||
|
return transaction.TargetAccount.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transaction.OriginAccount.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Schaad.Accounting.UI/Components/Routes.razor
Normal file
6
Schaad.Accounting.UI/Components/Routes.razor
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<Router AppAssembly="typeof(Program).Assembly">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||||
|
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
</Router>
|
||||||
16
Schaad.Accounting.UI/Components/_Imports.razor
Normal file
16
Schaad.Accounting.UI/Components/_Imports.razor
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.FluentUI.AspNetCore.Components
|
||||||
|
@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using Plotly.Blazor
|
||||||
|
@using Schaad.Accounting.UI
|
||||||
|
@using Schaad.Accounting.UI.Components
|
||||||
|
@using Schaad.Accounting.Interfaces
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue