diff --git a/Core/Core/ClientApp/src/app/home/home.service.ts b/Core/Core/ClientApp/src/app/home/home.service.ts index ada340b3752f8f23d3828a3275c85c826340f0e0..f0e3d590785b75c3e031e1e3ff125f33dc0e68ca 100644 --- a/Core/Core/ClientApp/src/app/home/home.service.ts +++ b/Core/Core/ClientApp/src/app/home/home.service.ts @@ -11,7 +11,7 @@ export class HomeService { private ForecastPieChart: Chart; Expenses: number[]; - TrasactionTypes: number[]; + TrasactionTypes: Array<{ name: string, y: number }>; ForecastTypes: number[]; dataSource: MatTableDataSource<FutureTransaction>; @@ -26,23 +26,21 @@ export class HomeService { this.LoadForecastData(); } - private LoadHistoryData():void { + private LoadHistoryData(): void { this.HttpClient.get<TransactionSumsContainer>('api/History').subscribe(res => { - console.log('Get history data: ', res); this.Expenses = res.expenses; - this.TrasactionTypes = new Array(res.smallCount, res.mediumCount, res.hugeCount); - console.log("redrawing data count:", this.Expenses.length); + //this.TrasactionTypes = new Array(res.smallCount, res.mediumCount, res.hugeCount); + console.log(res); + this.TrasactionTypes = this.MakePieChartData(res.expensesPerSize); this.ReloadHistoryChartSeries(); }, error => { - console.error("Deletion of user error:", error); + console.error("LoadHistoryData error:", error); }); } - private LoadForecastData():void { + private LoadForecastData(): void { this.HttpClient.get<TransactionSumsContainer>('api/Forecast').subscribe(res => { this.ForecastTypes = new Array(res.smallCount, res.mediumCount, res.hugeCount); - - console.log('Get Forecast data: ', res); let fileArray = new Array<FutureTransaction>(); for (let i: number = 0; i < res.expenses.length; i++) { fileArray.push({ position: i + 1, name: 'default', amount: res.expenses[i] }); @@ -54,6 +52,18 @@ export class HomeService { }); } + private MakePieChartData(input: Transaction[]): Array<{ name: string, y: number }> { + let pieData = new Array<{ name: string, y: number }>(); + + for (let i: number = 0; i < input.length; i++) { + if (input[i].amount > 0) + pieData.push({ name: input[i].name, y: input[i].amount }); + //pieData.push({ position: i + 1, name: 'default', amount: res.expenses[i] }); + } + + return pieData; + } + private ReloadHistoryChartSeries() { this.HistoryBarChart.removeSeries(0); this.HistoryBarChart.addSeries({ name: 'VĂ˝daje', data: this.Expenses } as Highcharts.SeriesColumnOptions, true, false); @@ -70,7 +80,7 @@ export class HomeService { this.ForecastPieChart.ref.reflow(); } - + public GetHistoryBar(): Chart { return this.HistoryBarChart; @@ -90,11 +100,18 @@ export class HomeService { type: 'column' }, title: { - text: 'Uplynulá hisotrie' + text: 'Ăštrata za poslednĂ ÄŤtyĹ™i tĂ˝dny' }, credits: { enabled: false }, + xAxis: { + categories: ['1. tĂ˝den', '2. tĂ˝den', '3. tĂ˝den', '4. tĂ˝den'] + }, + yAxis: { + labels: { format: '{value} KÄŤ' }, + title: { text: 'Výše Ăştraty (KÄŤ)' }, + }, series: [ { name: 'VĂ˝daje', @@ -140,11 +157,20 @@ export class HomeService { } } +export interface Transaction { + amount: number; + count: number; + name: string; +} + interface TransactionSumsContainer { expenses: number[]; smallCount: number; mediumCount: number; hugeCount: number; + expensesPerSymbolCategory: Transaction[]; + expensesPerSymbolType: Transaction[]; + expensesPerSize: Transaction[]; } export interface FutureTransaction { diff --git a/Core/Core/Controllers/HistoryController.cs b/Core/Core/Controllers/HistoryController.cs index 91b5ba3bad9ad7b06daf9bd8fd006c22050099ee..4f67247848b6cfa43cc70de68277c799cc98a05b 100644 --- a/Core/Core/Controllers/HistoryController.cs +++ b/Core/Core/Controllers/HistoryController.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Core.Controllers.containers; using Core.Data; using Core.Models; +using Core.Models.Codebooks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -45,35 +46,87 @@ namespace Core.Controllers { DateTime maxDate = transactions.Max(t => t.Date); + //create weekly intervals for transaction DateTime lowerbound = maxDate.AddDays(-4 * 7); lowerbound = new DateTime(lowerbound.Year, lowerbound.Month, lowerbound.Day); DateTime upperbound = maxDate.AddDays(-3 * 7); upperbound = new DateTime(upperbound.Year, upperbound.Month, upperbound.Day); - List<double> expenses = new List<double>(5); - int smallCount=0; - int mediumCount=0; - int hugeCount=0; + //assign category to transactions 4 week old + IQueryable<Transaction> customInterval = transactions.Where(t => t.Amount < 0 && t.Date >= lowerbound); + var symbolCategories = GetTransactionSymbolCategories(customInterval); + var transactionSizes = GetTransactionTypes(customInterval); + //group transactions by weeks + List<double> expenses = new List<double>(5); while (upperbound <= maxDate) { - var intervalTransactions = transactions - .Where(t => t.Date >= lowerbound && t.Date < upperbound); - - var dbg = intervalTransactions.ToList(); + IQueryable<Transaction> intervalTransactions = transactions + .Where(t => t.Amount < 0 && t.Date >= lowerbound && t.Date < upperbound); - expenses.Add((double)intervalTransactions.Where(t => t.Amount < 0).Sum(t => Math.Abs(t.Amount))); - smallCount += intervalTransactions.Where(t => t.Amount < -2 && t.Amount >= -500).Count(); - mediumCount += intervalTransactions.Where(t => t.Amount < -500 && t.Amount >= -5000).Count(); - hugeCount += intervalTransactions.Where(t => t.Amount < -5000).Count(); + expenses.Add((double)intervalTransactions.Sum(t => Math.Abs(t.Amount))); lowerbound = lowerbound.AddDays(7); upperbound = upperbound.AddDays(7); } - return new TransactionSumsContainer(expenses,smallCount,mediumCount,hugeCount); + return new TransactionSumsContainer(expenses, symbolCategories.PaymentCategories, symbolCategories.PaymentTypes, transactionSizes); + } + + private bool IsInInterval(int low, int high, decimal value) => value > low && value <= high; + + //Add count as tooltip + private List<TransactionContainer> GetTransactionTypes(IQueryable<Transaction> transactions) + { + return new List<TransactionContainer>() + { + new TransactionContainer((double) transactions.Where(t => IsInInterval(2,500,Math.Abs(t.Amount))).Sum(t => Math.Abs(t.Amount)),"DrobnĂ©"), + new TransactionContainer((double) transactions.Where(t => IsInInterval(500,5000,Math.Abs(t.Amount))).Sum(t => Math.Abs(t.Amount)),"MalĂ©"), + new TransactionContainer((double) transactions.Where(t => IsInInterval(5000,25000,Math.Abs(t.Amount))).Sum(t => Math.Abs(t.Amount)),"StĹ™ednĂ"), + new TransactionContainer((double) transactions.Where(t => IsInInterval(25000,50000,Math.Abs(t.Amount))).Sum(t => Math.Abs(t.Amount)),"VelkĂ©"), + new TransactionContainer((double) transactions.Where(t => IsInInterval(50000,500000,Math.Abs(t.Amount))).Sum(t => Math.Abs(t.Amount)),"Velmi velkĂ©"), + new TransactionContainer((double) transactions.Where(t => Math.Abs(t.Amount)>500000).Sum(t => Math.Abs(t.Amount)),"ZnaÄŤnÄ› velkĂ©") + }; } + private List<TransactionContainer> MakeGroup(IQueryable<Tuple<double, AbstractConstantSymbolVariation>> transactions, double undeterminedAmount) + { + const string labelUndefined = "NezaĹ™azeno"; + + List<TransactionContainer> list = transactions.GroupBy(s => s.Item2) + .Select(group => new TransactionContainer( + group.Sum(x => x.Item1), + group.First().Item2 != null ? group.First().Item2.Value : labelUndefined + )).ToList(); + + + int index = list.FindIndex(t => t.name == labelUndefined); + + if (index == -1) + list.Add(new TransactionContainer(undeterminedAmount, labelUndefined)); + else + list[index] = new TransactionContainer(undeterminedAmount + list[index].amount, labelUndefined); + + return list; + } + + private (List<TransactionContainer> PaymentTypes, List<TransactionContainer> PaymentCategories) GetTransactionSymbolCategories(IQueryable<Transaction> transactions) + { + var symbols = transactions + .Where(t => t.ConstantSymbol != null) + .Select(t => new { t.Amount, t.ConstantSymbol }); + + decimal undeterminedAmount = transactions.Where(t => t.ConstantSymbol == null || t.ConstantSymbol.LastNumber == null).Sum(t => t.Amount); + var paymentTypes = MakeGroup(symbols.Select(t => new Tuple<double, AbstractConstantSymbolVariation>((double)t.Amount, t.ConstantSymbol.LastNumber)), (double)undeterminedAmount); + + undeterminedAmount = transactions.Where(t => t.ConstantSymbol == null || t.ConstantSymbol.MinistryPredefined == null).Sum(t => t.Amount); + var paymentCategories = MakeGroup(symbols.Select(t => new Tuple<double, AbstractConstantSymbolVariation>((double)t.Amount, t.ConstantSymbol.MinistryPredefined)), (double)undeterminedAmount); + + return (paymentTypes, paymentCategories); + } + + + // POST: api/History [HttpPost] public void Post([FromBody] string value) diff --git a/Core/Core/Controllers/containers/TransactionContainer.cs b/Core/Core/Controllers/containers/TransactionContainer.cs new file mode 100644 index 0000000000000000000000000000000000000000..1be1565ce6b2cf3adc99e3d47041e1dd9d544c1f --- /dev/null +++ b/Core/Core/Controllers/containers/TransactionContainer.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Core.Controllers.containers +{ + public class TransactionContainer + { + public readonly double amount; + public readonly int count; + public readonly string name; + + public TransactionContainer(double amount, string name) + { + this.amount = amount; + this.name = name; + } + } +} diff --git a/Core/Core/Controllers/containers/TransactionSumsContainer.cs b/Core/Core/Controllers/containers/TransactionSumsContainer.cs index 748e7fce8a0af22cf0664a342fd072cd6cdf53ef..62127a51640993efd4c8b2e34b25af6286099e97 100644 --- a/Core/Core/Controllers/containers/TransactionSumsContainer.cs +++ b/Core/Core/Controllers/containers/TransactionSumsContainer.cs @@ -8,10 +8,20 @@ namespace Core.Controllers.containers public class TransactionSumsContainer { public readonly List<double> expenses; + public readonly int smallCount; public readonly int mediumCount; public readonly int hugeCount; + //TransactionContainer.name = constant symbol + //TransactionContainer.amount = amount per selcted constant symbol + public readonly List<TransactionContainer> expensesPerSymbolCategory; + public readonly List<TransactionContainer> expensesPerSymbolType; + + //TransactionContainer.name = type of transaction (small, big, very big) + //TransactionContainer.amount = amount per type + public readonly List<TransactionContainer> expensesPerSize; + public TransactionSumsContainer(List<double> expenses, int smallCount, int mediumCount, int hugeCount) { this.expenses = expenses; @@ -19,5 +29,13 @@ namespace Core.Controllers.containers this.mediumCount = mediumCount; this.hugeCount = hugeCount; } + + public TransactionSumsContainer(List<double> expenses, List<TransactionContainer> expensesPerSymbolCategory, List<TransactionContainer> expensesPerSymbolType, List<TransactionContainer> expensesPerSize) + { + this.expenses = expenses; + this.expensesPerSymbolCategory = expensesPerSymbolCategory; + this.expensesPerSymbolType = expensesPerSymbolType; + this.expensesPerSize = expensesPerSize; + } } }