public IDictionary <IFormattedClassification, double> AggregatePostings(IEnumerable <IPosting> postings, IList <IPostingClassification> postingClassifications, IErrorsAndInfos errorsAndInfos) { var result = new Dictionary <IFormattedClassification, double>(FormattedClassificationComparer); var resultDrillDown = new Dictionary <string, IList <IPosting> >(); foreach (var posting in postings) { var classifications = postingClassifications.Where(c => PostingClassificationMatcher.DoesPostingMatchClassification(posting, c)).ToList(); var combinedClassifications = classifications.Select(c => PostingClassificationFormatter.Format(c).CombinedClassification).Distinct().ToList(); switch (combinedClassifications.Count) { case 0 when Math.Abs(posting.Amount) <= 70: continue; case 0: errorsAndInfos.Errors.Add($"Amount of {posting.Amount} ('{posting.Remark}') could not be classified"); continue; case > 1 when posting.Amount != 0: var combinedClassification0 = PostingClassificationFormatter.Format(classifications[0]).CombinedClassification; var combinedClassification1 = PostingClassificationFormatter.Format(classifications[1]).CombinedClassification; errorsAndInfos.Errors.Add($"Classification of '{posting.Remark}' is ambiguous between '{combinedClassification0}' and '{combinedClassification1}'"); break; } var classification = classifications[0]; var formattedClassification = PostingClassificationFormatter.Format(classification); var formattedClassificationToString = formattedClassification.ToString() ?? ""; if (!result.ContainsKey(formattedClassification)) { result[formattedClassification] = 0; resultDrillDown[formattedClassificationToString] = new List <IPosting>(); } var amount = classification.IsMonthClassification ? posting.Amount : Math.Abs(posting.Amount); CalculationLogger.RegisterContribution(formattedClassificationToString, amount, posting); result[formattedClassification] += amount; resultDrillDown[formattedClassificationToString].Add(posting); } return(result); }
private bool IsPostingRelevantHere(IPosting posting, IEnumerable <IPostingClassification> postingClassifications, DateTime minDate, double minAmount, out IPostingClassification classification) { classification = null; if (Math.Abs(posting.Amount) < minAmount) { return(false); } if (posting.Date < minDate) { return(false); } var classifications = postingClassifications.Where(c => PostingClassificationMatcher.DoesPostingMatchClassification(posting, c)).ToList(); if (classifications.Count != 1) { return(false); } classification = classifications[0]; return(true); }
public async Task CalculateAndShowSummaryAsync(IList <IPosting> allPostings, IList <IPostingClassification> postingClassifications) { var errorsAndInfos = new ErrorsAndInfos(); var fairPostings = allPostings.Where(p => postingClassifications.FirstOrDefault(c => PostingClassificationMatcher.DoesPostingMatchClassification(p, c))?.Unfair != true).ToList(); var pureDebitCreditAggregation = PostingAggregator.AggregatePostings(fairPostings, new List <IPostingClassification> { new PostingClassification { Credit = false, Clue = "", Classification = "Debit" }, new PostingClassification { Credit = true, Clue = "", Classification = "Credit" } }, errorsAndInfos); if (errorsAndInfos.AnyErrors()) { await DataPresenter.WriteErrorsAsync(errorsAndInfos); return; } var overallSumList = pureDebitCreditAggregation.Select( result => new TypeItemSum { Type = result.Key.Sign, Item = result.Key.Classification, Sum = result.Value } ).Cast <ICollectionViewSourceEntity>().ToList(); await DataPresenter.Handlers.OverallSumsHandler.CollectionChangedAsync(overallSumList); errorsAndInfos = new ErrorsAndInfos(); var detailedAggregation = PostingAggregator.AggregatePostings(allPostings, postingClassifications, errorsAndInfos).OrderBy(result => result.Key.CombinedClassification).ToList(); if (errorsAndInfos.AnyErrors()) { await DataPresenter.WriteErrorsAsync(errorsAndInfos); return; } if (detailedAggregation.Any()) { var classificationSumList = detailedAggregation.Select( result => new TypeItemSum { Type = result.Key.Sign, Item = result.Key.Classification, Sum = result.Value } ).Cast <ICollectionViewSourceEntity>().ToList(); await DataPresenter.Handlers.ClassificationSumsHandler.CollectionChangedAsync(classificationSumList); } }
public async Task CalculateAndShowMonthlyDeltaAsync(IList <IPosting> allPostings, IList <IPostingClassification> postingClassifications) { var fairPostings = allPostings.Where(p => postingClassifications.FirstOrDefault(c => PostingClassificationMatcher.DoesPostingMatchClassification(p, c))?.Unfair != true).ToList(); var minYear = fairPostings.Min(p => p.Date.Year); var years = Enumerable.Range(minYear, DateTime.Today.Year - minYear + 1); var monthsClassifications = new List <IPostingClassification>(); foreach (var year in years) { for (var month = 1; month <= 12; month++) { var classification = postingClassifications.FirstOrDefault(c => c.Month == month && c.Year == year); if (classification != null) { monthsClassifications.Add(classification); } else { monthsClassifications.Add(new PostingClassification { Month = month, Year = year, Classification = $"Δ {year}-{month:00}" }); } } } var errorsAndInfos = new ErrorsAndInfos(); var monthlyDeltas = PostingAggregator.AggregatePostings(fairPostings, monthsClassifications, errorsAndInfos).OrderByDescending(result => result.Key.CombinedClassification).ToList(); if (errorsAndInfos.AnyErrors()) { await DataPresenter.WriteErrorsAsync(errorsAndInfos); return; } var monthlyDeltasList = monthlyDeltas.Select( result => new TypeMonthDelta { Type = "Δ", Month = result.Key.Classification.Replace("Δ", "").Trim(), Delta = result.Value } ).Cast <ICollectionViewSourceEntity>().ToList(); await DataPresenter.Handlers.MonthlyDeltasHandler.CollectionChangedAsync(monthlyDeltasList); }